| @@ -0,0 +1,20 @@ | ||
| import mock | ||
| import unittest | ||
| class TestInitializeDB(unittest.TestCase): | ||
| @mock.patch('tutorial.scripts.initializedb.sys') | ||
| def test_usage(self, mocked_sys): | ||
| from ..scripts.initializedb import main | ||
| main(argv=['foo']) | ||
| mocked_sys.exit.assert_called_with(1) | ||
| @mock.patch('tutorial.scripts.initializedb.get_tm_session') | ||
| @mock.patch('tutorial.scripts.initializedb.sys') | ||
| def test_run(self, mocked_sys, mocked_session): | ||
| from ..scripts.initializedb import main | ||
| main(argv=['foo', 'development.ini']) | ||
| mocked_session.assert_called_once() | ||
| @@ -0,0 +1,21 @@ | ||
| import mock | ||
| import unittest | ||
| class TestMyAuthenticationPolicy(unittest.TestCase): | ||
| def test_no_user(self): | ||
| request = mock.Mock() | ||
| request.user = None | ||
| from ..security import MyAuthenticationPolicy | ||
| policy = MyAuthenticationPolicy(None) | ||
| self.assertEqual(policy.authenticated_userid(request), None) | ||
| def test_authenticated_user(self): | ||
| request = mock.Mock() | ||
| request.user.id = 'foo' | ||
| from ..security import MyAuthenticationPolicy | ||
| policy = MyAuthenticationPolicy(None) | ||
| self.assertEqual(policy.authenticated_userid(request), 'foo') |
| @@ -0,0 +1,67 @@ | ||
| import unittest | ||
| import transaction | ||
| from pyramid import testing | ||
| class BaseTest(unittest.TestCase): | ||
| def setUp(self): | ||
| from ..models import get_tm_session | ||
| self.config = testing.setUp(settings={ | ||
| 'sqlalchemy.url': 'sqlite:///:memory:' | ||
| }) | ||
| self.config.include('..models') | ||
| self.config.include('..routes') | ||
| session_factory = self.config.registry['dbsession_factory'] | ||
| self.session = get_tm_session(session_factory, transaction.manager) | ||
| self.init_database() | ||
| def init_database(self): | ||
| from ..models.meta import Base | ||
| session_factory = self.config.registry['dbsession_factory'] | ||
| engine = session_factory.kw['bind'] | ||
| Base.metadata.create_all(engine) | ||
| def tearDown(self): | ||
| testing.tearDown() | ||
| transaction.abort() | ||
| def makeUser(self, name, role): | ||
| from ..models import User | ||
| return User(name=name, role=role) | ||
| class TestSetPassword(BaseTest): | ||
| def test_password_hash_saved(self): | ||
| user = self.makeUser(name='foo', role='bar') | ||
| self.assertFalse(user.password_hash) | ||
| user.set_password('secret') | ||
| self.assertTrue(user.password_hash) | ||
| class TestCheckPassword(BaseTest): | ||
| def test_password_hash_not_set(self): | ||
| user = self.makeUser(name='foo', role='bar') | ||
| self.assertFalse(user.password_hash) | ||
| self.assertFalse(user.check_password('secret')) | ||
| def test_correct_password(self): | ||
| user = self.makeUser(name='foo', role='bar') | ||
| user.set_password('secret') | ||
| self.assertTrue(user.password_hash) | ||
| self.assertTrue(user.check_password('secret')) | ||
| def test_incorrect_password(self): | ||
| user = self.makeUser(name='foo', role='bar') | ||
| user.set_password('secret') | ||
| self.assertTrue(user.password_hash) | ||
| self.assertFalse(user.check_password('incorrect')) |
| @@ -0,0 +1,338 @@ | ||
| .. _typographical-conventions: | ||
| Typographical Conventions | ||
| ========================= | ||
| .. meta:: | ||
| :description: This chapter describes typographical conventions used in the Pyramid documentation. | ||
| :keywords: Pyramid, Typographical Conventions | ||
| .. _typographical-conventions-introduction: | ||
| Introduction | ||
| ------------ | ||
| This chapter describes typographical conventions used in the Pyramid documentation. Documentation authors and contributors should review the :ref:`style-guide`. | ||
| .. _typographical-conventions-glossary: | ||
| Glossary | ||
| -------- | ||
| A glossary defines terms used throughout the documentation. References to glossary terms appear as follows. | ||
| :term:`request` | ||
| Note it is hyperlinked, and when clicked it will take the user to the term in the Glossary and highlight the term. | ||
| .. _typographical-conventions-links: | ||
| Links | ||
| ----- | ||
| Links are presented as follows, and may be clickable. | ||
| `TryPyramid <https://TryPyramid.com>`_ | ||
| .. seealso:: See also :ref:`typographical-conventions-cross-references` for other links within the documentation. | ||
| .. _typographical-conventions-topic: | ||
| Topic | ||
| ----- | ||
| A topic is similar to a block quote with a title, or a self-contained section with no subsections. A topic indicates a self-contained idea that is separate from the flow of the document. Topics may occur anywhere a section or transition may occur. | ||
| .. topic:: Topic Title | ||
| Subsequent indented lines comprise | ||
| the body of the topic, and are | ||
| interpreted as body elements. | ||
| .. _typographical-conventions-displaying-code: | ||
| Code | ||
| ---- | ||
| Code may be displayed in blocks or inline. Blocks of code may use syntax highlighting, line numbering, and emphasis. | ||
| .. _typographical-conventions-syntax-highlighting: | ||
| Syntax highlighting | ||
| ^^^^^^^^^^^^^^^^^^^ | ||
| XML: | ||
| .. code-block:: xml | ||
| <somesnippet>Some XML</somesnippet> | ||
| Unix shell commands are prefixed with a ``$`` character. (See :term:`venv` for the meaning of ``$VENV``.) | ||
| .. code-block:: bash | ||
| $ $VENV/bin/pip install -e . | ||
| Windows commands are prefixed with a drive letter with an optional directory name. (See :term:`venv` for the meaning of ``%VENV%``.) | ||
| .. code-block:: doscon | ||
| c:\> %VENV%\Scripts\pcreate -s starter MyProject | ||
| cfg: | ||
| .. code-block:: cfg | ||
| [some-part] | ||
| # A random part in the buildout | ||
| recipe = collective.recipe.foo | ||
| option = value | ||
| ini: | ||
| .. code-block:: ini | ||
| [nosetests] | ||
| match=^test | ||
| where=pyramid | ||
| nocapture=1 | ||
| Interactive Python: | ||
| .. code-block:: pycon | ||
| >>> class Foo: | ||
| ... bar = 100 | ||
| ... | ||
| >>> f = Foo() | ||
| >>> f.bar | ||
| 100 | ||
| >>> f.bar / 0 | ||
| Traceback (most recent call last): | ||
| File "<stdin>", line 1, in <module> | ||
| ZeroDivisionError: integer division or modulo by zero | ||
| .. _typographical-conventions-long-commands: | ||
| Displaying long commands | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| When a command that should be typed on one line is too long to fit on the displayed width of a page, the backslash character ``\`` is used to indicate that the subsequent printed line should be part of the command: | ||
| .. code-block:: bash | ||
| $ $VENV/bin/py.test tutorial/tests.py --cov-report term-missing \ | ||
| --cov=tutorial -q | ||
| .. _typographical-conventions-code-block-options: | ||
| Code block options | ||
| ^^^^^^^^^^^^^^^^^^ | ||
| To emphasize lines, we give the appearance that a highlighting pen has been used on the code. | ||
| .. code-block:: python | ||
| :emphasize-lines: 1,3 | ||
| if "foo" == "bar": | ||
| # This is Python code | ||
| pass | ||
| A code block with line numbers. | ||
| .. code-block:: python | ||
| :linenos: | ||
| if "foo" == "bar": | ||
| # This is Python code | ||
| pass | ||
| Some code blocks may be given a caption. | ||
| .. code-block:: python | ||
| :caption: sample.py | ||
| :name: sample-py-typographical-conventions | ||
| if "foo" == "bar": | ||
| # This is Python code | ||
| pass | ||
| .. _typographical-conventions-inline-code: | ||
| Inline code | ||
| ^^^^^^^^^^^ | ||
| Inline code is displayed as follows, where the inline code is 'pip install -e ".[docs]"'. | ||
| Install requirements for building documentation: ``pip install -e ".[docs]"`` | ||
| .. _typographical-conventions-feature-versioning: | ||
| Feature versioning | ||
| ------------------ | ||
| We designate the version in which something is added, changed, or deprecated in the project. | ||
| .. _typographical-conventions-version-added: | ||
| Version added | ||
| ^^^^^^^^^^^^^ | ||
| The version in which a feature is added to a project is displayed as follows. | ||
| .. versionadded:: 1.1 | ||
| :func:`pyramid.paster.bootstrap` | ||
| .. _typographical-conventions-version-changed: | ||
| Version changed | ||
| ^^^^^^^^^^^^^^^ | ||
| The version in which a feature is changed in a project is displayed as follows. | ||
| .. versionchanged:: 1.8 | ||
| Added the ability for ``bootstrap`` to cleanup automatically via the ``with`` statement. | ||
| .. _typographical-conventions-deprecated: | ||
| Deprecated | ||
| ^^^^^^^^^^ | ||
| The version in which a feature is deprecated in a project is displayed as follows. | ||
| .. deprecated:: 1.7 | ||
| Use the ``require_csrf`` option or read :ref:`auto_csrf_checking` instead to have :class:`pyramid.exceptions.BadCSRFToken` exceptions raised. | ||
| .. _typographical-conventions-danger: | ||
| Danger | ||
| ------ | ||
| Danger represents critical information related to a topic or concept, and should recommend to the user "don't do this dangerous thing". | ||
| .. danger:: | ||
| This is danger or an error. | ||
| .. _typographical-conventions-warnings: | ||
| Warnings | ||
| -------- | ||
| Warnings represent limitations and advice related to a topic or concept. | ||
| .. warning:: | ||
| This is a warning. | ||
| .. _typographical-conventions-notes: | ||
| Notes | ||
| ----- | ||
| Notes represent additional information related to a topic or concept. | ||
| .. note:: | ||
| This is a note. | ||
| .. _typographical-conventions-see-also: | ||
| See also | ||
| -------- | ||
| "See also" messages refer to topics that are related to the current topic, but have a narrative tone to them instead of merely a link without explanation. "See also" is rendered in a block as well, so that it stands out for the reader's attention. | ||
| .. seealso:: | ||
| See :ref:`Quick Tutorial section on Requirements <qtut_requirements>`. | ||
| .. _typographical-conventions-todo: | ||
| Todo | ||
| ---- | ||
| Todo items designated tasks that require further work. | ||
| .. todo:: | ||
| This is a todo item. | ||
| .. _typographical-conventions-cross-references: | ||
| Cross-references | ||
| ---------------- | ||
| Cross-references are links that may be to a document, arbitrary location, object, or other items. | ||
| .. _typographical-conventions-cross-referencing-documents: | ||
| Cross-referencing documents | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| Links to pages within this documentation display as follows. | ||
| :doc:`quick_tour` | ||
| .. _typographical-conventions-cross-referencing-arbitrary-locations: | ||
| Cross-referencing arbitrary locations | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| Links to sections, and tables and figures with captions, within this documentation display as follows. | ||
| :ref:`i18n_chapter` | ||
| .. _typographical-conventions-cross-referencing-python: | ||
| Python modules, classes, methods, and functions | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| All of the following are clickable links to Python modules, classes, methods, and functions. | ||
| Python module names display as follows. | ||
| :mod:`pyramid.config` | ||
| Python class names display as follows. | ||
| :class:`pyramid.config.Configurator` | ||
| Python method names display as follows. | ||
| :meth:`pyramid.config.Configurator.add_view` | ||
| Python function names display as follows. | ||
| :func:`pyramid.renderers.render_to_response` | ||
| Sometimes we show only the last segment of a Python object's name, which displays as follows. | ||
| :func:`~pyramid.renderers.render_to_response` | ||
| The application "Pyramid" itself displays as follows. | ||
| :app:`Pyramid` | ||
| @@ -1,301 +1,2 @@ | ||
| import re | ||
| from pyramid.exceptions import ConfigurationError | ||
| from pyramid.compat import is_nonstr_iter | ||
| from pyramid.traversal import ( | ||
| find_interface, | ||
| traversal_path, | ||
| resource_path_tuple | ||
| ) | ||
| from pyramid.urldispatch import _compile_route | ||
| from pyramid.util import object_description | ||
| from pyramid.session import check_csrf_token | ||
| from .util import as_sorted_tuple | ||
| _marker = object() | ||
| class XHRPredicate(object): | ||
| def __init__(self, val, config): | ||
| self.val = bool(val) | ||
| def text(self): | ||
| return 'xhr = %s' % self.val | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| return bool(request.is_xhr) is self.val | ||
| class RequestMethodPredicate(object): | ||
| def __init__(self, val, config): | ||
| request_method = as_sorted_tuple(val) | ||
| if 'GET' in request_method and 'HEAD' not in request_method: | ||
| # GET implies HEAD too | ||
| request_method = as_sorted_tuple(request_method + ('HEAD',)) | ||
| self.val = request_method | ||
| def text(self): | ||
| return 'request_method = %s' % (','.join(self.val)) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| return request.method in self.val | ||
| class PathInfoPredicate(object): | ||
| def __init__(self, val, config): | ||
| self.orig = val | ||
| try: | ||
| val = re.compile(val) | ||
| except re.error as why: | ||
| raise ConfigurationError(why.args[0]) | ||
| self.val = val | ||
| def text(self): | ||
| return 'path_info = %s' % (self.orig,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| return self.val.match(request.upath_info) is not None | ||
| class RequestParamPredicate(object): | ||
| def __init__(self, val, config): | ||
| val = as_sorted_tuple(val) | ||
| reqs = [] | ||
| for p in val: | ||
| k = p | ||
| v = None | ||
| if p.startswith('='): | ||
| if '=' in p[1:]: | ||
| k, v = p[1:].split('=', 1) | ||
| k = '=' + k | ||
| k, v = k.strip(), v.strip() | ||
| elif '=' in p: | ||
| k, v = p.split('=', 1) | ||
| k, v = k.strip(), v.strip() | ||
| reqs.append((k, v)) | ||
| self.val = val | ||
| self.reqs = reqs | ||
| def text(self): | ||
| return 'request_param %s' % ','.join( | ||
| ['%s=%s' % (x,y) if y else x for x, y in self.reqs] | ||
| ) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| for k, v in self.reqs: | ||
| actual = request.params.get(k) | ||
| if actual is None: | ||
| return False | ||
| if v is not None and actual != v: | ||
| return False | ||
| return True | ||
| class HeaderPredicate(object): | ||
| def __init__(self, val, config): | ||
| name = val | ||
| v = None | ||
| if ':' in name: | ||
| name, val_str = name.split(':', 1) | ||
| try: | ||
| v = re.compile(val_str) | ||
| except re.error as why: | ||
| raise ConfigurationError(why.args[0]) | ||
| if v is None: | ||
| self._text = 'header %s' % (name,) | ||
| else: | ||
| self._text = 'header %s=%s' % (name, val_str) | ||
| self.name = name | ||
| self.val = v | ||
| def text(self): | ||
| return self._text | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| if self.val is None: | ||
| return self.name in request.headers | ||
| val = request.headers.get(self.name) | ||
| if val is None: | ||
| return False | ||
| return self.val.match(val) is not None | ||
| class AcceptPredicate(object): | ||
| def __init__(self, val, config): | ||
| self.val = val | ||
| def text(self): | ||
| return 'accept = %s' % (self.val,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| return self.val in request.accept | ||
| class ContainmentPredicate(object): | ||
| def __init__(self, val, config): | ||
| self.val = config.maybe_dotted(val) | ||
| def text(self): | ||
| return 'containment = %s' % (self.val,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| ctx = getattr(request, 'context', context) | ||
| return find_interface(ctx, self.val) is not None | ||
| class RequestTypePredicate(object): | ||
| def __init__(self, val, config): | ||
| self.val = val | ||
| def text(self): | ||
| return 'request_type = %s' % (self.val,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| return self.val.providedBy(request) | ||
| class MatchParamPredicate(object): | ||
| def __init__(self, val, config): | ||
| val = as_sorted_tuple(val) | ||
| self.val = val | ||
| reqs = [ p.split('=', 1) for p in val ] | ||
| self.reqs = [ (x.strip(), y.strip()) for x, y in reqs ] | ||
| def text(self): | ||
| return 'match_param %s' % ','.join( | ||
| ['%s=%s' % (x,y) for x, y in self.reqs] | ||
| ) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| if not request.matchdict: | ||
| # might be None | ||
| return False | ||
| for k, v in self.reqs: | ||
| if request.matchdict.get(k) != v: | ||
| return False | ||
| return True | ||
| class CustomPredicate(object): | ||
| def __init__(self, func, config): | ||
| self.func = func | ||
| def text(self): | ||
| return getattr( | ||
| self.func, | ||
| '__text__', | ||
| 'custom predicate: %s' % object_description(self.func) | ||
| ) | ||
| def phash(self): | ||
| # using hash() here rather than id() is intentional: we | ||
| # want to allow custom predicates that are part of | ||
| # frameworks to be able to define custom __hash__ | ||
| # functions for custom predicates, so that the hash output | ||
| # of predicate instances which are "logically the same" | ||
| # may compare equal. | ||
| return 'custom:%r' % hash(self.func) | ||
| def __call__(self, context, request): | ||
| return self.func(context, request) | ||
| class TraversePredicate(object): | ||
| # Can only be used as a *route* "predicate"; it adds 'traverse' to the | ||
| # matchdict if it's specified in the routing args. This causes the | ||
| # ResourceTreeTraverser to use the resolved traverse pattern as the | ||
| # traversal path. | ||
| def __init__(self, val, config): | ||
| _, self.tgenerate = _compile_route(val) | ||
| self.val = val | ||
| def text(self): | ||
| return 'traverse matchdict pseudo-predicate' | ||
| def phash(self): | ||
| # This isn't actually a predicate, it's just a infodict modifier that | ||
| # injects ``traverse`` into the matchdict. As a result, we don't | ||
| # need to update the hash. | ||
| return '' | ||
| def __call__(self, context, request): | ||
| if 'traverse' in context: | ||
| return True | ||
| m = context['match'] | ||
| tvalue = self.tgenerate(m) # tvalue will be urlquoted string | ||
| m['traverse'] = traversal_path(tvalue) | ||
| # This isn't actually a predicate, it's just a infodict modifier that | ||
| # injects ``traverse`` into the matchdict. As a result, we just | ||
| # return True. | ||
| return True | ||
| class CheckCSRFTokenPredicate(object): | ||
| check_csrf_token = staticmethod(check_csrf_token) # testing | ||
| def __init__(self, val, config): | ||
| self.val = val | ||
| def text(self): | ||
| return 'check_csrf = %s' % (self.val,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| val = self.val | ||
| if val: | ||
| if val is True: | ||
| val = 'csrf_token' | ||
| return self.check_csrf_token(request, val, raises=False) | ||
| return True | ||
| class PhysicalPathPredicate(object): | ||
| def __init__(self, val, config): | ||
| if is_nonstr_iter(val): | ||
| self.val = tuple(val) | ||
| else: | ||
| val = tuple(filter(None, val.split('/'))) | ||
| self.val = ('',) + val | ||
| def text(self): | ||
| return 'physical_path = %s' % (self.val,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| if getattr(context, '__name__', _marker) is not _marker: | ||
| return resource_path_tuple(context) == self.val | ||
| return False | ||
| class EffectivePrincipalsPredicate(object): | ||
| def __init__(self, val, config): | ||
| if is_nonstr_iter(val): | ||
| self.val = set(val) | ||
| else: | ||
| self.val = set((val,)) | ||
| def text(self): | ||
| return 'effective_principals = %s' % sorted(list(self.val)) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| req_principals = request.effective_principals | ||
| if is_nonstr_iter(req_principals): | ||
| rpset = set(req_principals) | ||
| if self.val.issubset(rpset): | ||
| return True | ||
| return False | ||
| import zope.deprecation | ||
| zope.deprecation.moved('pyramid.predicates', 'Pyramid 2.0') |
| @@ -0,0 +1,300 @@ | ||
| import re | ||
| from pyramid.exceptions import ConfigurationError | ||
| from pyramid.compat import is_nonstr_iter | ||
| from pyramid.session import check_csrf_token | ||
| from pyramid.traversal import ( | ||
| find_interface, | ||
| traversal_path, | ||
| resource_path_tuple | ||
| ) | ||
| from pyramid.urldispatch import _compile_route | ||
| from pyramid.util import object_description | ||
| from pyramid.util import as_sorted_tuple | ||
| _marker = object() | ||
| class XHRPredicate(object): | ||
| def __init__(self, val, config): | ||
| self.val = bool(val) | ||
| def text(self): | ||
| return 'xhr = %s' % self.val | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| return bool(request.is_xhr) is self.val | ||
| class RequestMethodPredicate(object): | ||
| def __init__(self, val, config): | ||
| request_method = as_sorted_tuple(val) | ||
| if 'GET' in request_method and 'HEAD' not in request_method: | ||
| # GET implies HEAD too | ||
| request_method = as_sorted_tuple(request_method + ('HEAD',)) | ||
| self.val = request_method | ||
| def text(self): | ||
| return 'request_method = %s' % (','.join(self.val)) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| return request.method in self.val | ||
| class PathInfoPredicate(object): | ||
| def __init__(self, val, config): | ||
| self.orig = val | ||
| try: | ||
| val = re.compile(val) | ||
| except re.error as why: | ||
| raise ConfigurationError(why.args[0]) | ||
| self.val = val | ||
| def text(self): | ||
| return 'path_info = %s' % (self.orig,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| return self.val.match(request.upath_info) is not None | ||
| class RequestParamPredicate(object): | ||
| def __init__(self, val, config): | ||
| val = as_sorted_tuple(val) | ||
| reqs = [] | ||
| for p in val: | ||
| k = p | ||
| v = None | ||
| if p.startswith('='): | ||
| if '=' in p[1:]: | ||
| k, v = p[1:].split('=', 1) | ||
| k = '=' + k | ||
| k, v = k.strip(), v.strip() | ||
| elif '=' in p: | ||
| k, v = p.split('=', 1) | ||
| k, v = k.strip(), v.strip() | ||
| reqs.append((k, v)) | ||
| self.val = val | ||
| self.reqs = reqs | ||
| def text(self): | ||
| return 'request_param %s' % ','.join( | ||
| ['%s=%s' % (x,y) if y else x for x, y in self.reqs] | ||
| ) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| for k, v in self.reqs: | ||
| actual = request.params.get(k) | ||
| if actual is None: | ||
| return False | ||
| if v is not None and actual != v: | ||
| return False | ||
| return True | ||
| class HeaderPredicate(object): | ||
| def __init__(self, val, config): | ||
| name = val | ||
| v = None | ||
| if ':' in name: | ||
| name, val_str = name.split(':', 1) | ||
| try: | ||
| v = re.compile(val_str) | ||
| except re.error as why: | ||
| raise ConfigurationError(why.args[0]) | ||
| if v is None: | ||
| self._text = 'header %s' % (name,) | ||
| else: | ||
| self._text = 'header %s=%s' % (name, val_str) | ||
| self.name = name | ||
| self.val = v | ||
| def text(self): | ||
| return self._text | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| if self.val is None: | ||
| return self.name in request.headers | ||
| val = request.headers.get(self.name) | ||
| if val is None: | ||
| return False | ||
| return self.val.match(val) is not None | ||
| class AcceptPredicate(object): | ||
| def __init__(self, val, config): | ||
| self.val = val | ||
| def text(self): | ||
| return 'accept = %s' % (self.val,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| return self.val in request.accept | ||
| class ContainmentPredicate(object): | ||
| def __init__(self, val, config): | ||
| self.val = config.maybe_dotted(val) | ||
| def text(self): | ||
| return 'containment = %s' % (self.val,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| ctx = getattr(request, 'context', context) | ||
| return find_interface(ctx, self.val) is not None | ||
| class RequestTypePredicate(object): | ||
| def __init__(self, val, config): | ||
| self.val = val | ||
| def text(self): | ||
| return 'request_type = %s' % (self.val,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| return self.val.providedBy(request) | ||
| class MatchParamPredicate(object): | ||
| def __init__(self, val, config): | ||
| val = as_sorted_tuple(val) | ||
| self.val = val | ||
| reqs = [ p.split('=', 1) for p in val ] | ||
| self.reqs = [ (x.strip(), y.strip()) for x, y in reqs ] | ||
| def text(self): | ||
| return 'match_param %s' % ','.join( | ||
| ['%s=%s' % (x,y) for x, y in self.reqs] | ||
| ) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| if not request.matchdict: | ||
| # might be None | ||
| return False | ||
| for k, v in self.reqs: | ||
| if request.matchdict.get(k) != v: | ||
| return False | ||
| return True | ||
| class CustomPredicate(object): | ||
| def __init__(self, func, config): | ||
| self.func = func | ||
| def text(self): | ||
| return getattr( | ||
| self.func, | ||
| '__text__', | ||
| 'custom predicate: %s' % object_description(self.func) | ||
| ) | ||
| def phash(self): | ||
| # using hash() here rather than id() is intentional: we | ||
| # want to allow custom predicates that are part of | ||
| # frameworks to be able to define custom __hash__ | ||
| # functions for custom predicates, so that the hash output | ||
| # of predicate instances which are "logically the same" | ||
| # may compare equal. | ||
| return 'custom:%r' % hash(self.func) | ||
| def __call__(self, context, request): | ||
| return self.func(context, request) | ||
| class TraversePredicate(object): | ||
| # Can only be used as a *route* "predicate"; it adds 'traverse' to the | ||
| # matchdict if it's specified in the routing args. This causes the | ||
| # ResourceTreeTraverser to use the resolved traverse pattern as the | ||
| # traversal path. | ||
| def __init__(self, val, config): | ||
| _, self.tgenerate = _compile_route(val) | ||
| self.val = val | ||
| def text(self): | ||
| return 'traverse matchdict pseudo-predicate' | ||
| def phash(self): | ||
| # This isn't actually a predicate, it's just a infodict modifier that | ||
| # injects ``traverse`` into the matchdict. As a result, we don't | ||
| # need to update the hash. | ||
| return '' | ||
| def __call__(self, context, request): | ||
| if 'traverse' in context: | ||
| return True | ||
| m = context['match'] | ||
| tvalue = self.tgenerate(m) # tvalue will be urlquoted string | ||
| m['traverse'] = traversal_path(tvalue) | ||
| # This isn't actually a predicate, it's just a infodict modifier that | ||
| # injects ``traverse`` into the matchdict. As a result, we just | ||
| # return True. | ||
| return True | ||
| class CheckCSRFTokenPredicate(object): | ||
| check_csrf_token = staticmethod(check_csrf_token) # testing | ||
| def __init__(self, val, config): | ||
| self.val = val | ||
| def text(self): | ||
| return 'check_csrf = %s' % (self.val,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| val = self.val | ||
| if val: | ||
| if val is True: | ||
| val = 'csrf_token' | ||
| return self.check_csrf_token(request, val, raises=False) | ||
| return True | ||
| class PhysicalPathPredicate(object): | ||
| def __init__(self, val, config): | ||
| if is_nonstr_iter(val): | ||
| self.val = tuple(val) | ||
| else: | ||
| val = tuple(filter(None, val.split('/'))) | ||
| self.val = ('',) + val | ||
| def text(self): | ||
| return 'physical_path = %s' % (self.val,) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| if getattr(context, '__name__', _marker) is not _marker: | ||
| return resource_path_tuple(context) == self.val | ||
| return False | ||
| class EffectivePrincipalsPredicate(object): | ||
| def __init__(self, val, config): | ||
| if is_nonstr_iter(val): | ||
| self.val = set(val) | ||
| else: | ||
| self.val = set((val,)) | ||
| def text(self): | ||
| return 'effective_principals = %s' % sorted(list(self.val)) | ||
| phash = text | ||
| def __call__(self, context, request): | ||
| req_principals = request.effective_principals | ||
| if is_nonstr_iter(req_principals): | ||
| rpset = set(req_principals) | ||
| if self.val.issubset(rpset): | ||
| return True | ||
| return False | ||
| @@ -2,7 +2,7 @@ | ||
| # (http://pythonpaste.org) Licensed under the MIT license: | ||
| # http://www.opensource.org/licenses/mit-license.php | ||
| import optparse | ||
| import argparse | ||
| import os | ||
| import os.path | ||
| import pkg_resources | ||
| @@ -12,6 +12,7 @@ | ||
| _bad_chars_re = re.compile('[^a-zA-Z0-9_]') | ||
| def main(argv=sys.argv, quiet=False): | ||
| command = PCreateCommand(argv, quiet) | ||
| try: | ||
| @@ -21,73 +22,77 @@ def main(argv=sys.argv, quiet=False): | ||
| class PCreateCommand(object): | ||
| verbosity = 1 # required | ||
| description = "Render Pyramid scaffolding to an output directory" | ||
| usage = "usage: %prog [options] -s <scaffold> output_directory" | ||
| parser = optparse.OptionParser(usage, description=description) | ||
| parser.add_option('-s', '--scaffold', | ||
| dest='scaffold_name', | ||
| action='append', | ||
| help=("Add a scaffold to the create process " | ||
| "(multiple -s args accepted)")) | ||
| parser.add_option('-t', '--template', | ||
| dest='scaffold_name', | ||
| action='append', | ||
| help=('A backwards compatibility alias for ' | ||
| '-s/--scaffold. Add a scaffold to the ' | ||
| 'create process (multiple -t args accepted)')) | ||
| parser.add_option('-l', '--list', | ||
| dest='list', | ||
| action='store_true', | ||
| help="List all available scaffold names") | ||
| parser.add_option('--list-templates', | ||
| dest='list', | ||
| action='store_true', | ||
| help=("A backwards compatibility alias for -l/--list. " | ||
| "List all available scaffold names.")) | ||
| parser.add_option('--package-name', | ||
| dest='package_name', | ||
| action='store', | ||
| type='string', | ||
| help='Package name to use. The name provided is assumed ' | ||
| 'to be a valid Python package name, and will not ' | ||
| 'be validated. By default the package name is ' | ||
| 'derived from the value of output_directory.') | ||
| parser.add_option('--simulate', | ||
| dest='simulate', | ||
| action='store_true', | ||
| help='Simulate but do no work') | ||
| parser.add_option('--overwrite', | ||
| dest='overwrite', | ||
| action='store_true', | ||
| help='Always overwrite') | ||
| parser.add_option('--interactive', | ||
| dest='interactive', | ||
| action='store_true', | ||
| help='When a file would be overwritten, interrogate ' | ||
| '(this is the default, but you may specify it to ' | ||
| 'override --overwrite)') | ||
| parser.add_option('--ignore-conflicting-name', | ||
| dest='force_bad_name', | ||
| action='store_true', | ||
| default=False, | ||
| help='Do create a project even if the chosen name ' | ||
| 'is the name of an already existing / importable ' | ||
| 'package.') | ||
| verbosity = 1 # required | ||
| parser = argparse.ArgumentParser( | ||
| description="Render Pyramid scaffolding to an output directory") | ||
| parser.add_argument('-s', '--scaffold', | ||
| dest='scaffold_name', | ||
| action='append', | ||
| help=("Add a scaffold to the create process " | ||
| "(multiple -s args accepted)")) | ||
| parser.add_argument('-t', '--template', | ||
| dest='scaffold_name', | ||
| action='append', | ||
| help=('A backwards compatibility alias for ' | ||
| '-s/--scaffold. Add a scaffold to the ' | ||
| 'create process (multiple -t args accepted)')) | ||
| parser.add_argument('-l', '--list', | ||
| dest='list', | ||
| action='store_true', | ||
| help="List all available scaffold names") | ||
| parser.add_argument('--list-templates', | ||
| dest='list', | ||
| action='store_true', | ||
| help=("A backwards compatibility alias for -l/--list. " | ||
| "List all available scaffold names.")) | ||
| parser.add_argument('--package-name', | ||
| dest='package_name', | ||
| action='store', | ||
| help='Package name to use. The name provided is ' | ||
| 'assumed to be a valid Python package name, and ' | ||
| 'will not be validated. By default the package ' | ||
| 'name is derived from the value of ' | ||
| 'output_directory.') | ||
| parser.add_argument('--simulate', | ||
| dest='simulate', | ||
| action='store_true', | ||
| help='Simulate but do no work') | ||
| parser.add_argument('--overwrite', | ||
| dest='overwrite', | ||
| action='store_true', | ||
| help='Always overwrite') | ||
| parser.add_argument('--interactive', | ||
| dest='interactive', | ||
| action='store_true', | ||
| help='When a file would be overwritten, interrogate ' | ||
| '(this is the default, but you may specify it to ' | ||
| 'override --overwrite)') | ||
| parser.add_argument('--ignore-conflicting-name', | ||
| dest='force_bad_name', | ||
| action='store_true', | ||
| default=False, | ||
| help='Do create a project even if the chosen name ' | ||
| 'is the name of an already existing / importable ' | ||
| 'package.') | ||
| parser.add_argument('output_directory', | ||
|
||
| nargs = '?', | ||
| default = None, | ||
| help='The directory where the project will be ' | ||
| 'created.') | ||
| pyramid_dist = pkg_resources.get_distribution("pyramid") | ||
| def __init__(self, argv, quiet=False): | ||
| self.quiet = quiet | ||
| self.options, self.args = self.parser.parse_args(argv[1:]) | ||
| if not self.options.interactive and not self.options.overwrite: | ||
| self.options.interactive = True | ||
| self.args = self.parser.parse_args(argv[1:]) | ||
| if not self.args.interactive and not self.args.overwrite: | ||
| self.args.interactive = True | ||
| self.scaffolds = self.all_scaffolds() | ||
| def run(self): | ||
| if self.options.list: | ||
| if self.args.list: | ||
| return self.show_scaffolds() | ||
| if not self.options.scaffold_name and not self.args: | ||
| if not self.args.scaffold_name and not self.args.output_directory: | ||
| if not self.quiet: # pragma: no cover | ||
| self.parser.print_help() | ||
| self.out('') | ||
| @@ -101,18 +106,18 @@ def run(self): | ||
| @property | ||
| def output_path(self): | ||
| return os.path.abspath(os.path.normpath(self.args[0])) | ||
| return os.path.abspath(os.path.normpath(self.args.output_directory)) | ||
| @property | ||
| def project_vars(self): | ||
| output_dir = self.output_path | ||
| project_name = os.path.basename(os.path.split(output_dir)[1]) | ||
| if self.options.package_name is None: | ||
| if self.args.package_name is None: | ||
| pkg_name = _bad_chars_re.sub( | ||
| '', project_name.lower().replace('-', '_')) | ||
| safe_name = pkg_resources.safe_name(project_name) | ||
| else: | ||
| pkg_name = self.options.package_name | ||
| pkg_name = self.args.package_name | ||
| safe_name = pkg_name | ||
| egg_name = pkg_resources.to_filename(safe_name) | ||
| @@ -145,7 +150,7 @@ def project_vars(self): | ||
| def render_scaffolds(self): | ||
| props = self.project_vars | ||
| output_dir = self.output_path | ||
| for scaffold_name in self.options.scaffold_name: | ||
| for scaffold_name in self.args.scaffold_name: | ||
| for scaffold in self.scaffolds: | ||
| if scaffold.name == scaffold_name: | ||
| scaffold.run(self, output_dir, props) | ||
| @@ -182,23 +187,23 @@ def out(self, msg): # pragma: no cover | ||
| print(msg) | ||
| def validate_input(self): | ||
| if not self.options.scaffold_name: | ||
| if not self.args.scaffold_name: | ||
| self.out('You must provide at least one scaffold name: -s <scaffold name>') | ||
| self.out('') | ||
| self.show_scaffolds() | ||
| return False | ||
| if not self.args: | ||
| if not self.args.output_directory: | ||
| self.out('You must provide a project name') | ||
| return False | ||
| available = [x.name for x in self.scaffolds] | ||
| diff = set(self.options.scaffold_name).difference(available) | ||
| diff = set(self.args.scaffold_name).difference(available) | ||
| if diff: | ||
| self.out('Unavailable scaffolds: %s' % ", ".join(sorted(diff))) | ||
| return False | ||
| pkg_name = self.project_vars['package'] | ||
| if pkg_name == 'site' and not self.options.force_bad_name: | ||
| if pkg_name == 'site' and not self.args.force_bad_name: | ||
| self.out('The package name "site" has a special meaning in ' | ||
| 'Python. Are you sure you want to use it as your ' | ||
| 'project\'s name?') | ||
| @@ -214,7 +219,7 @@ def validate_input(self): | ||
| if not pkg_exists: | ||
| return True | ||
| if self.options.force_bad_name: | ||
| if self.args.force_bad_name: | ||
| return True | ||
| self.out('A package named "{0}" already exists, are you sure you want ' | ||
| 'to use it as your project\'s name?'.format(pkg_name)) | ||
You'll need to make this optional if you want the program to behave the same as before... otherwise argparse will helpfully sys.exit() and let the user know they need to specify an output directory...