diff options
author | Tres Seaver <tseaver@palladion.com> | 2013-03-08 14:38:02 -0500 |
---|---|---|
committer | Tres Seaver <tseaver@palladion.com> | 2013-03-08 14:38:02 -0500 |
commit | aa830118284fc1c5a6881f8258f39e046a28ce79 (patch) | |
tree | 4a64e35121eac495f14c9d7405c30ba6dad69d79 | |
parent | d59c575269bafa1429608f9433bdaccb0f5568ee (diff) | |
parent | 21bedd54cead429a36545ab4107cd7b1b1b08417 (diff) | |
download | zope-security-aa830118284fc1c5a6881f8258f39e046a28ce79.tar.gz |
Merge branch 'master' into pure_python_proxy
-rw-r--r-- | .travis.yml | 12 | ||||
-rw-r--r-- | CHANGES.rst | 54 | ||||
-rw-r--r-- | TODO-4.0.txt | 10 | ||||
-rw-r--r-- | bootstrap.py | 186 | ||||
-rw-r--r-- | buildout.cfg | 2 | ||||
-rw-r--r-- | docs/api/checker.rst | 113 | ||||
-rw-r--r-- | docs/api/decorator.rst | 25 | ||||
-rw-r--r-- | docs/api/permission.rst | 10 | ||||
-rw-r--r-- | docs/api/zcml.rst | 14 | ||||
-rw-r--r-- | setup.cfg | 1 | ||||
-rw-r--r-- | setup.py | 26 | ||||
-rw-r--r-- | src/zope/security/_proxy.c | 10 | ||||
-rw-r--r-- | src/zope/security/_zope_security_checker.c | 21 | ||||
-rw-r--r-- | src/zope/security/checker.py | 8 | ||||
-rw-r--r-- | src/zope/security/examples/sandbox.py | 2 | ||||
-rw-r--r-- | src/zope/security/metaconfigure.py | 4 | ||||
-rw-r--r-- | src/zope/security/testing.py | 19 | ||||
-rw-r--r-- | src/zope/security/tests/test_proxy.py | 16 | ||||
-rw-r--r-- | tox.ini | 3 |
19 files changed, 325 insertions, 211 deletions
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6c854d4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: python +python: + - 2.6 + - 2.7 + - 3.2 + - 3.3 +install: + - pip install . --use-mirrors +script: + - python setup.py test -q +notifications: + email: false diff --git a/CHANGES.rst b/CHANGES.rst index 5678dbf..66ba415 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,16 +2,38 @@ CHANGES ======= -4.0.0 (unreleased) ------------------- +4.0.0a6 (unreleased) +-------------------- + +- fixed extension compilation on windows python 3.x + + +4.0.0a5 (2013-02-28) +-------------------- -- TODO: Add PyPy support +- Undo changes from 4.0.0a4. Instead, ``zope.untrustedpython`` is only + included during Python 2 installs. -- TODO: add pure-Python implementations of: - - ``z.s._proxy.getChecker`` - - ``z.s._proxy.getObject`` - - ``z.s._proxy._Proxy`` +4.0.0a4 (2013-02-28) +-------------------- + +- Remove ``untrustedpython`` extra again, since we do not want to support + ``zope.untrustedpython`` in ZTK 2.0. If BBB is really needed, we will create + a 3.10.0 release. + +4.0.0a3 (2013-02-15) +-------------------- + +- Fix test breakage in 4.0.0a2 due to deprecation strategy. + +4.0.0a2 (2013-02-15) +-------------------- + +- Added back the ``untrustedpython`` extra: now pulls in + ``zope.untrustedpython``. Restored deprecated backward-compatible imports + for ``zope.security.untrustedpython.{builtins,interpreter,rcompile}`` + (the extra and the imports are to be removed in version 4.1). 4.0.0a1 (2013-02-14) @@ -65,6 +87,24 @@ CHANGES - Added test convenience helper ``create_interaction`` and ``with interaction()``. +3.9.0 (2012-12-21) +------------------ + +- Pin ``zope.proxy >= 4.1.0`` + +- Ship with an included ``proxy.h`` header which is compatible with the + 4.1.x version ov ``zope.proxy``. + +3.8.5 (2012-12-21) +------------------ + +- Ship with an included ``proxy.h`` header which is compatible with the + supported versions of ``zope.proxy``. + +3.8.4 (2012-12-20) +------------------ + +- Pin ``zope.proxy >= 3.4.2, <4.1dev`` 3.8.3 (2011-09-24) ------------------ diff --git a/TODO-4.0.txt b/TODO-4.0.txt new file mode 100644 index 0000000..bfae913 --- /dev/null +++ b/TODO-4.0.txt @@ -0,0 +1,10 @@ +4.0.0 TODO +---------- + +- TODO: Add PyPy support + +- TODO: add pure-Python implementations of: + + - ``z.s._proxy.getChecker`` + - ``z.s._proxy.getObject`` + - ``z.s._proxy._Proxy`` diff --git a/bootstrap.py b/bootstrap.py index a6da5d3..ec3757a 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -18,102 +18,148 @@ The script accepts buildout command-line options, so you can use the -c option to specify an alternate configuration file. """ -import os, shutil, sys, tempfile, urllib2 +import os, shutil, sys, tempfile from optparse import OptionParser tmpeggs = tempfile.mkdtemp() -is_jython = sys.platform.startswith('java') +usage = '''\ +[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options] -# parsing arguments -parser = OptionParser() -parser.add_option("-v", "--version", dest="version", - help="use a specific zc.buildout version") -parser.add_option("-d", "--distribute", - action="store_true", dest="distribute", default=False, - help="Use Disribute rather than Setuptools.") +Bootstraps a buildout-based project. -parser.add_option("-c", None, action="store", dest="config_file", +Simply run this script in a directory containing a buildout.cfg, using the +Python that you want bin/buildout to use. + +Note that by using --setup-source and --download-base to point to +local resources, you can keep this script from going over the network. +''' + +parser = OptionParser(usage=usage) +parser.add_option("-v", "--version", help="use a specific zc.buildout version") + +parser.add_option("-t", "--accept-buildout-test-releases", + dest='accept_buildout_test_releases', + action="store_true", default=False, + help=("Normally, if you do not specify a --version, the " + "bootstrap script and buildout gets the newest " + "*final* versions of zc.buildout and its recipes and " + "extensions for you. If you use this flag, " + "bootstrap and buildout will get the newest releases " + "even if they are alphas or betas.")) +parser.add_option("-c", "--config-file", help=("Specify the path to the buildout configuration " "file to be used.")) +parser.add_option("-f", "--find-links", + help=("Specify a URL to search for buildout releases")) -options, args = parser.parse_args() - -# if -c was provided, we push it back into args for buildout' main function -if options.config_file is not None: - args += ['-c', options.config_file] -if options.version is not None: - VERSION = '==%s' % options.version -else: - VERSION = '' +options, args = parser.parse_args() -USE_DISTRIBUTE = options.distribute -args = args + ['bootstrap'] +###################################################################### +# load/install distribute to_reload = False try: - import pkg_resources + import pkg_resources, setuptools if not hasattr(pkg_resources, '_distribute'): to_reload = True raise ImportError except ImportError: ez = {} - if USE_DISTRIBUTE: - exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py' - ).read() in ez - ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True) - else: - exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' - ).read() in ez - ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) + + try: + from urllib.request import urlopen + except ImportError: + from urllib2 import urlopen + + exec(urlopen('http://python-distribute.org/distribute_setup.py').read(), ez) + setup_args = dict(to_dir=tmpeggs, download_delay=0, no_fake=True) + ez['use_setuptools'](**setup_args) if to_reload: reload(pkg_resources) - else: - import pkg_resources - -if sys.platform == 'win32': - def quote(c): - if ' ' in c: - return '"%s"' % c # work around spawn lamosity on windows - else: - return c -else: - def quote (c): - return c - -cmd = 'from setuptools.command.easy_install import main; main()' + import pkg_resources + # This does not (always?) update the default working set. We will + # do it. + for path in sys.path: + if path not in pkg_resources.working_set.entries: + pkg_resources.working_set.add_entry(path) + +###################################################################### +# Install buildout + ws = pkg_resources.working_set -if USE_DISTRIBUTE: - requirement = 'distribute' -else: - requirement = 'setuptools' - -if is_jython: - import subprocess - - assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd', - quote(tmpeggs), 'zc.buildout' + VERSION], - env=dict(os.environ, - PYTHONPATH= - ws.find(pkg_resources.Requirement.parse(requirement)).location - ), - ).wait() == 0 - -else: - assert os.spawnle( - os.P_WAIT, sys.executable, quote (sys.executable), - '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION, - dict(os.environ, - PYTHONPATH= - ws.find(pkg_resources.Requirement.parse(requirement)).location - ), - ) == 0 +cmd = [sys.executable, '-c', + 'from setuptools.command.easy_install import main; main()', + '-mZqNxd', tmpeggs] + +find_links = os.environ.get( + 'bootstrap-testing-find-links', + options.find_links or + ('http://downloads.buildout.org/' + if options.accept_buildout_test_releases else None) + ) +if find_links: + cmd.extend(['-f', find_links]) + +distribute_path = ws.find( + pkg_resources.Requirement.parse('distribute')).location + +requirement = 'zc.buildout' +version = options.version +if version is None and not options.accept_buildout_test_releases: + # Figure out the most recent final version of zc.buildout. + import setuptools.package_index + _final_parts = '*final-', '*final' + def _final_version(parsed_version): + for part in parsed_version: + if (part[:1] == '*') and (part not in _final_parts): + return False + return True + index = setuptools.package_index.PackageIndex( + search_path=[distribute_path]) + if find_links: + index.add_find_links((find_links,)) + req = pkg_resources.Requirement.parse(requirement) + if index.obtain(req) is not None: + best = [] + bestv = None + for dist in index[req.project_name]: + distv = dist.parsed_version + if _final_version(distv): + if bestv is None or distv > bestv: + best = [dist] + bestv = distv + elif distv == bestv: + best.append(dist) + if best: + best.sort() + version = best[-1].version +if version: + requirement = '=='.join((requirement, version)) +cmd.append(requirement) + +import subprocess +if subprocess.call(cmd, env=dict(os.environ, PYTHONPATH=distribute_path)) != 0: + raise Exception( + "Failed to execute command:\n%s", + repr(cmd)[1:-1]) + +###################################################################### +# Import and run buildout ws.add_entry(tmpeggs) -ws.require('zc.buildout' + VERSION) +ws.require(requirement) import zc.buildout.buildout + +if not [a for a in args if '=' not in a]: + args.append('bootstrap') + +# if -c was provided, we push it back into args for buildout' main function +if options.config_file is not None: + args[0:0] = ['-c', options.config_file] + zc.buildout.buildout.main(args) shutil.rmtree(tmpeggs) diff --git a/buildout.cfg b/buildout.cfg index 5d65d3b..0a7118d 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -14,7 +14,7 @@ eggs = [python] recipe = zc.recipe.egg eggs = - zope.security + zope.security [test,zcml,pytz,untrustedpython] interpreter = python [coverage-test] diff --git a/docs/api/checker.rst b/docs/api/checker.rst index 8b321db..58d3595 100644 --- a/docs/api/checker.rst +++ b/docs/api/checker.rst @@ -82,8 +82,7 @@ directly, or via interface: >>> context = AContext() >>> allow(context, attributes=['foo', 'bar'], interface=[I1, I2]) - >>> context.actions.sort( - ... lambda a, b: cmp(a['discriminator'], b['discriminator'])) + >>> context.actions.sort(key=lambda a: a['discriminator']) >>> pprint(context.actions) [{'args': ('testmodule', 'a', 'zope.Public'), 'callable': 1, @@ -136,8 +135,7 @@ directly, or via interface: >>> require(context, attributes=['foo', 'bar'], ... interface=[I1, I2], permission='p') - >>> context.actions.sort( - ... lambda a, b: cmp(a['discriminator'], b['discriminator'])) + >>> context.actions.sort(key=lambda a: a['discriminator']) >>> pprint(context.actions) [{'args': ('testmodule', 'a', 'p'), 'callable': 1, @@ -182,26 +180,26 @@ Protections for standard objects ... from zope.security.interfaces import ForbiddenAttribute ... try: ... return getattr(object, attr) - ... except ForbiddenAttribute, e: - ... return 'ForbiddenAttribute: %s' % e[0] + ... except ForbiddenAttribute as e: + ... return 'ForbiddenAttribute: %s' % e.args[0] >>> def check_forbidden_setitem(object, item, value): ... from zope.security.interfaces import ForbiddenAttribute ... try: ... object[item] = value - ... except ForbiddenAttribute, e: - ... return 'ForbiddenAttribute: %s' % e[0] + ... except ForbiddenAttribute as e: + ... return 'ForbiddenAttribute: %s' % e.args[0] >>> def check_forbidden_delitem(object, item): ... from zope.security.interfaces import ForbiddenAttribute ... try: ... del object[item] - ... except ForbiddenAttribute, e: - ... return 'ForbiddenAttribute: %s' % e[0] + ... except ForbiddenAttribute as e: + ... return 'ForbiddenAttribute: %s' % e.args[0] >>> def check_forbidden_call(callable, *args): # ** ... from zope.security.interfaces import ForbiddenAttribute ... try: ... return callable(*args) # ** - ... except ForbiddenAttribute, e: - ... return 'ForbiddenAttribute: %s' % e[0] + ... except ForbiddenAttribute as e: + ... return 'ForbiddenAttribute: %s' % e.args[0] Rocks ##### @@ -217,16 +215,12 @@ Rocks are immuatle, non-callable objects without interesting methods. They 1 >>> int(type(ProxyFactory( 1.0 )) is float) 1 - >>> int(type(ProxyFactory( 1l )) is long) - 1 >>> int(type(ProxyFactory( 1j )) is complex) 1 >>> int(type(ProxyFactory( None )) is type(None)) 1 >>> int(type(ProxyFactory( 'xxx' )) is str) 1 - >>> int(type(ProxyFactory( u'xxx' )) is unicode) - 1 >>> int(type(ProxyFactory( True )) is type(True)) 1 @@ -270,18 +264,18 @@ We can do everything we expect to be able to do with proxied dicts. 1 >>> len(d) 2 - >>> list(d) + >>> sorted(list(d)) ['a', 'b'] >>> d.get('a') 1 - >>> int(d.has_key('a')) + >>> int('a' in d) 1 >>> c = d.copy() >>> check_forbidden_get(c, 'clear') 'ForbiddenAttribute: clear' >>> int(str(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}")) 1 - >>> int(`c` in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}")) + >>> int(repr(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}")) 1 >>> def sorted(x): ... x = list(x) @@ -293,27 +287,12 @@ We can do everything we expect to be able to do with proxied dicts. [1, 2] >>> sorted(d.items()) [('a', 1), ('b', 2)] - >>> sorted(d.iterkeys()) - ['a', 'b'] - >>> sorted(d.itervalues()) - [1, 2] - >>> sorted(d.iteritems()) - [('a', 1), ('b', 2)] -Always available: +Always available (note, that dicts in python-3.x are not orderable, so we are +not checking that under python > 2): .. doctest:: - >>> int(d < d) - 0 - >>> int(d > d) - 0 - >>> int(d <= d) - 1 - >>> int(d >= d) - 1 - >>> int(d == d) - 1 >>> int(d != d) 0 >>> int(bool(d)) @@ -351,7 +330,7 @@ We can do everything we expect to be able to do with proxied lists. 1 >>> str(l) '[1, 2]' - >>> `l` + >>> repr(l) '[1, 2]' >>> l + l [1, 2, 1, 2] @@ -398,7 +377,7 @@ We can do everything we expect to be able to do with proxied tuples. 1 >>> str(l) '(1, 2)' - >>> `l` + >>> repr(l) '(1, 2)' >>> l + l (1, 2, 1, 2) @@ -607,8 +586,8 @@ we can do everything we expect to be able to do with proxied frozensets. ... from zope.security.interfaces import ForbiddenAttribute ... try: ... return getattr(object, attr) - ... except ForbiddenAttribute, e: - ... return 'ForbiddenAttribute: %s' % e[0] + ... except ForbiddenAttribute as e: + ... return 'ForbiddenAttribute: %s' % e.args[0] >>> from zope.security.checker import ProxyFactory >>> from zope.security.interfaces import ForbiddenAttribute >>> us = frozenset((1, 2)) @@ -779,6 +758,8 @@ iterators .. doctest:: + >>> [a for a in ProxyFactory(iter([1, 2]))] + [1, 2] >>> list(ProxyFactory(iter([1, 2]))) [1, 2] >>> list(ProxyFactory(iter((1, 2)))) @@ -815,7 +796,7 @@ We can iterate over sequences >>> from zope.security.checker import NamesChecker >>> from zope.security.checker import ProxyFactory - >>> c = NamesChecker(['__getitem__']) + >>> c = NamesChecker(['__getitem__', '__len__']) >>> p = ProxyFactory(x, c) Even if they are proxied @@ -856,7 +837,7 @@ New-style classes >>> check_forbidden_get(C, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(C) - >>> s = `C` + >>> s = repr(C) >>> int(C.__module__ == __name__) 1 >>> len(C.__bases__) @@ -868,14 +849,6 @@ Always available: .. doctest:: - >>> int(C < C) - 0 - >>> int(C > C) - 0 - >>> int(C <= C) - 1 - >>> int(C >= C) - 1 >>> int(C == C) 1 >>> int(C != C) @@ -907,14 +880,6 @@ Always available: .. doctest:: - >>> int(c < c) - 0 - >>> int(c > c) - 0 - >>> int(c <= c) - 1 - >>> int(c >= c) - 1 >>> int(c == c) 1 >>> int(c != c) @@ -938,24 +903,16 @@ Classic Classes >>> check_forbidden_get(C, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(C) - >>> s = `C` + >>> s = repr(C) >>> int(C.__module__ == __name__) 1 >>> len(C.__bases__) - 0 + 1 Always available: .. doctest:: - >>> int(C < C) - 0 - >>> int(C > C) - 0 - >>> int(C <= C) - 1 - >>> int(C >= C) - 1 >>> int(C == C) 1 >>> int(C != C) @@ -984,14 +941,6 @@ Always available: .. doctest:: - >>> int(c < c) - 0 - >>> int(c > c) - 0 - >>> int(c <= c) - 1 - >>> int(c >= c) - 1 >>> int(c == c) 1 >>> int(c != c) @@ -1055,7 +1004,7 @@ We work with the ABCMeta meta class: >>> check_forbidden_get(PBar, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(PBar) - >>> s = `PBar` + >>> s = repr(PBar) >>> int(PBar.__module__ == __name__) 1 >>> len(PBar.__bases__) @@ -1065,19 +1014,11 @@ Always available: .. doctest:: - >>> int(PBar < PBar) - 0 - >>> int(PBar > PBar) - 0 - >>> int(PBar <= PBar) - 1 - >>> int(PBar >= PBar) - 1 >>> int(PBar == PBar) 1 >>> int(PBar != PBar) 0 >>> int(bool(PBar)) 1 - >>> int(PBar.__class__ == abc.ABCMeta) + >>> int(PBar.__class__ == type) 1 diff --git a/docs/api/decorator.rst b/docs/api/decorator.rst index c4741b9..c05c692 100644 --- a/docs/api/decorator.rst +++ b/docs/api/decorator.rst @@ -48,13 +48,16 @@ Using `selectChecker()`, we can confirm that a `Foo` object uses .. doctest:: >>> from zope.security.checker import selectChecker + >>> from zope.security.interfaces import ForbiddenAttribute >>> foo = Foo() >>> selectChecker(foo) is fooChecker True >>> fooChecker.check(foo, 'a') - >>> fooChecker.check(foo, 'b') # doctest: +ELLIPSIS - Traceback (most recent call last): - ForbiddenAttribute: ('b', <zope.security.decorator.Foo object ...>) + >>> try: + ... fooChecker.check(foo, 'b') # doctest: +ELLIPSIS + ... except ForbiddenAttribute as e: + ... e + ForbiddenAttribute('b', <Foo object ...>) and that a `Wrapper` object uses `wrappeChecker`: @@ -64,9 +67,11 @@ and that a `Wrapper` object uses `wrappeChecker`: >>> selectChecker(wrapper) is wrapperChecker True >>> wrapperChecker.check(wrapper, 'b') - >>> wrapperChecker.check(wrapper, 'a') # doctest: +ELLIPSIS - Traceback (most recent call last): - ForbiddenAttribute: ('a', <zope.security.decorator.Foo object ...>) + >>> try: + ... wrapperChecker.check(wrapper, 'a') # doctest: +ELLIPSIS + ... except ForbiddenAttribute as e: + ... e + ForbiddenAttribute('a', <Foo object ...>) (Note that the object description says `Foo` because the object is a proxy and generally looks and acts like the object it's proxying.) @@ -94,9 +99,11 @@ illustrate, we'll proxify `foo`: >>> secure_foo = ProxyFactory(foo) >>> secure_foo.a 'a' - >>> secure_foo.b # doctest: +ELLIPSIS - Traceback (most recent call last): - ForbiddenAttribute: ('b', <zope.security.decorator.Foo object ...>) + >>> try: + ... secure_foo.b # doctest: +ELLIPSIS + ... except ForbiddenAttribute as e: + ... e + ForbiddenAttribute('b', <Foo object ...>) when we wrap the secured `foo`: diff --git a/docs/api/permission.rst b/docs/api/permission.rst index 64a4250..819cc90 100644 --- a/docs/api/permission.rst +++ b/docs/api/permission.rst @@ -48,7 +48,7 @@ The :data:`zope.security.checker.CheckerPublic` permission always exists: >>> ids = list(allPermissions(None)) >>> ids.sort() >>> ids - [u'x', u'y'] + ['x', 'y'] .. autofunction:: zope.security.permission.PermissionsVocabulary @@ -100,9 +100,9 @@ The non-public permissions 'x' and 'y' are string values: .. doctest:: >>> vocab.getTermByToken('x').value - u'x' + 'x' >>> vocab.getTermByToken('y').value - u'y' + 'y' However, the public permission value is CheckerPublic: @@ -116,7 +116,7 @@ and its title is shortened: .. doctest:: >>> vocab.getTermByToken('zope.Public').title - u'Public' + 'Public' The terms are sorted by title except for the public permission, which is listed first: @@ -124,7 +124,7 @@ listed first: .. doctest:: >>> [term.title for term in vocab] - [u'Public', u'x', u'y'] + ['Public', 'x', 'y'] .. testcleanup:: diff --git a/docs/api/zcml.rst b/docs/api/zcml.rst index 5fdc3a0..a23075a 100644 --- a/docs/api/zcml.rst +++ b/docs/api/zcml.rst @@ -11,8 +11,9 @@ a couple of permissions: >>> from zope.component import getGlobalSiteManager >>> from zope.configuration.xmlconfig import XMLConfig + >>> from zope.component.testing import setUp >>> import zope.security - + >>> setUp() # clear global component registry >>> XMLConfig('permissions.zcml', zope.security)() >>> len(list(getGlobalSiteManager().registeredUtilities())) @@ -64,10 +65,13 @@ Now let's see whether validation works alright >>> field._validate('zope.ManageCode') >>> context._actions[0]['args'] (None, 'zope.foo') - >>> field._validate('3 foo') - Traceback (most recent call last): - ... - InvalidId: 3 foo + + >>> from zope.schema.interfaces import InvalidId + >>> try: + ... field._validate('3 foo') + ... except InvalidId as e: + ... e + InvalidId('3 foo') zope.Public is always valid >>> field._validate('zope.Public') @@ -4,6 +4,7 @@ cover-package=zope.security cover-erase=1 with-doctest=0 where=src +exclude=untrustedpython [aliases] dev = develop easy_install zope.security[testing] @@ -26,12 +26,28 @@ from setuptools import find_packages from setuptools import setup TESTS_REQUIRE = [ - 'zope.testing', - 'zope.configuration', 'zope.component', + 'zope.configuration', 'zope.location', + 'zope.testing', + 'zope.testrunner', ] +def alltests(): + import os + import sys + import unittest + # use the zope.testrunner machinery to find all the + # test suites we've put under ourselves + import zope.testrunner.find + import zope.testrunner.options + here = os.path.abspath(os.path.join(os.path.dirname(__file__), 'src')) + args = sys.argv[:] + defaults = ["--test-path", here] + options = zope.testrunner.options.get_options(args, defaults) + suites = list(zope.testrunner.find.find_suites(options)) + return unittest.TestSuite(suites) + here = os.path.abspath(os.path.dirname(__file__)) def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() @@ -66,6 +82,7 @@ include = [ModuleHeaderDir('zope.proxy')] # Jython cannot build the C optimizations, while on PyPy they are # anti-optimizations (the C extension compatibility layer is known-slow, # and defeats JIT opportunities). +py3 = sys.version_info[0] >= 3 py_impl = getattr(platform, 'python_implementation', lambda: None) pure_python = os.environ.get('PURE_PYTHON', False) is_pypy = py_impl() == 'PyPy' @@ -88,7 +105,7 @@ else: ] setup(name='zope.security', - version='4.0.0dev', + version='4.0.0a6.dev0', author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', description='Zope Security Framework', @@ -130,10 +147,11 @@ setup(name='zope.security', 'zope.proxy >= 4.1.0', 'zope.schema', ], - test_suite = 'zope.security', + test_suite = '__main__.alltests', tests_require=TESTS_REQUIRE, extras_require = dict( pytz=["pytz"], + untrustedpython=['zope.untrustedpython'] if not py3 else [], zcml=['zope.configuration'], test=TESTS_REQUIRE, testing=TESTS_REQUIRE + ['nose', 'coverage'], diff --git a/src/zope/security/_proxy.c b/src/zope/security/_proxy.c index 52b5040..27edbb3 100644 --- a/src/zope/security/_proxy.c +++ b/src/zope/security/_proxy.c @@ -983,6 +983,11 @@ MOD_INIT(_proxy) { PyObject *m; + MOD_DEF(m, "_proxy", module___doc__, module_functions) + + if (m == NULL) + return MOD_ERROR_VAL; + if (Proxy_Import() < 0) return MOD_ERROR_VAL; @@ -1083,11 +1088,6 @@ if((str_op_##S = INTERN("__" #S "__")) == NULL) return MOD_ERROR_VAL if (PyType_Ready(&SecurityProxyType) < 0) return MOD_ERROR_VAL; - MOD_DEF(m, "_proxy", module___doc__, module_functions) - - if (m == NULL) - return MOD_ERROR_VAL; - Py_INCREF(&SecurityProxyType); PyModule_AddObject(m, "_Proxy", (PyObject *)&SecurityProxyType); diff --git a/src/zope/security/_zope_security_checker.c b/src/zope/security/_zope_security_checker.c index 1b91312..e5a2528 100644 --- a/src/zope/security/_zope_security_checker.c +++ b/src/zope/security/_zope_security_checker.c @@ -589,6 +589,14 @@ module_functions[] = { MOD_INIT(_zope_security_checker) { PyObject* m; + PyObject* mod; + + MOD_DEF(mod, "_zope_security_checker", module___doc__, module_functions) + + if (mod == NULL) + { + return MOD_ERROR_VAL; + } CheckerType.tp_new = PyType_GenericNew; if (PyType_Ready(&CheckerType) < 0) @@ -673,14 +681,7 @@ if((str_##S = INTERN(#S)) == NULL) return MOD_ERROR_VAL return MOD_ERROR_VAL; } - MOD_DEF(m, "_zope_security_checker", module___doc__, module_functions) - - if (m == NULL) - { - return MOD_ERROR_VAL; - } - -#define EXPORT(N) Py_INCREF(N); PyModule_AddObject(m, #N, N) +#define EXPORT(N) Py_INCREF(N); PyModule_AddObject(mod, #N, N) EXPORT(_checkers); EXPORT(NoProxy); @@ -688,7 +689,7 @@ if((str_##S = INTERN(#S)) == NULL) return MOD_ERROR_VAL EXPORT(_available_by_default); Py_INCREF(&CheckerType); - PyModule_AddObject(m, "Checker", (PyObject *)&CheckerType); + PyModule_AddObject(mod, "Checker", (PyObject *)&CheckerType); - return MOD_SUCCESS_VAL(m); + return MOD_SUCCESS_VAL(mod); } diff --git a/src/zope/security/checker.py b/src/zope/security/checker.py index e5f88dc..5f1d7df 100644 --- a/src/zope/security/checker.py +++ b/src/zope/security/checker.py @@ -595,7 +595,7 @@ _typeChecker = NamesChecker( '__implemented__']) _namedChecker = NamesChecker(['__name__']) -_iteratorChecker = NamesChecker(['next', '__iter__']) +_iteratorChecker = NamesChecker(['next', '__iter__', '__len__']) _setChecker = NamesChecker(['__iter__', '__len__', '__str__', '__contains__', 'copy', 'difference', 'intersection', 'issubset', @@ -642,9 +642,13 @@ _basic_types = { datetime.time: NoProxy, datetime.tzinfo: NoProxy, } -if PYTHON2: +if PYTHON2: _basic_types[long] = NoProxy _basic_types[unicode] = NoProxy +else: #pragma NO COVER + _basic_types[type({}.values())] = NoProxy + _basic_types[type({}.keys())] = NoProxy + _basic_types[type({}.items())] = NoProxy try: import pytz diff --git a/src/zope/security/examples/sandbox.py b/src/zope/security/examples/sandbox.py index cb2455c..8a17984 100644 --- a/src/zope/security/examples/sandbox.py +++ b/src/zope/security/examples/sandbox.py @@ -253,7 +253,7 @@ class TimeGenerator(object): home.transportAgent(a, new_home) except Exception as e: print('-- Exception --') - print('moving "%s" from "%s" to "%s"' %(a, h,` new_home`)) + print('moving "%s" from "%s" to "%s"' %(a, h, repr(new_home))) print(e) print() self.teardownAgent(a) diff --git a/src/zope/security/metaconfigure.py b/src/zope/security/metaconfigure.py index 54484ac..e2dd0a0 100644 --- a/src/zope/security/metaconfigure.py +++ b/src/zope/security/metaconfigure.py @@ -109,7 +109,7 @@ class ClassDirective(object): def __protectByInterface(self, interface, permission_id): "Set a permission on names in an interface." - for n, d in interface.namesAndDescriptions(1): + for n, d in sorted(interface.namesAndDescriptions(1)): self.__protectName(n, permission_id) self.__context.action( discriminator = None, @@ -143,7 +143,7 @@ class ClassDirective(object): def __protectSetSchema(self, schema, permission_id): "Set a permission on a bunch of names." _context = self.__context - for name in schema: + for name in sorted(schema): field = schema[name] if IField.providedBy(field) and not field.readonly: _context.action( diff --git a/src/zope/security/testing.py b/src/zope/security/testing.py index 53d1ebe..ee13db8 100644 --- a/src/zope/security/testing.py +++ b/src/zope/security/testing.py @@ -15,13 +15,30 @@ This module provides some helper/stub objects for setting up interactions. """ +import sys +import re from zope import interface, component from zope.security import interfaces from zope.security.permission import Permission import contextlib import zope.security.management - +from zope.testing import renormalizing + +PY2 = sys.version_info[0] == 2 + +if PY2: + _u = unicode + rules = [(re.compile("b('.*?')"), r"\1"), + (re.compile('b(".*?")'), r"\1"), + ] + output_checker = renormalizing.RENormalizing(rules) +else: + _u = str + rules = [(re.compile("u('.*?')"), r"\1"), + (re.compile('u(".*?")'), r"\1"), + ] + output_checker = renormalizing.RENormalizing(rules) @interface.implementer(interfaces.IPrincipal) class Principal: diff --git a/src/zope/security/tests/test_proxy.py b/src/zope/security/tests/test_proxy.py index 877616d..523cd03 100644 --- a/src/zope/security/tests/test_proxy.py +++ b/src/zope/security/tests/test_proxy.py @@ -14,6 +14,7 @@ """Security proxy tests """ import unittest +import sys from zope.security._compat import PYTHON2 @@ -35,6 +36,15 @@ def _skip_if_Py2(testfunc): return dummy return testfunc +def _fmt_address(obj): + # Try to replicate PyString_FromString("%p", obj), which actually uses + # the platform sprintf(buf, "%p", obj), which we cannot access from Python + # directly (and ctypes seems like overkill). + if sys.platform == 'win32': + return '0x%08x' % id(obj) + else: + return '0x%0x' % id(obj) + class ProxyTestBase(object): @@ -153,9 +163,10 @@ class ProxyTestBase(object): target = object() checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) + address = _fmt_address(target) self.assertEqual(str(proxy), '<security proxied %s.object ' - 'instance at 0x%0x>' % (_BUILTINS, id(target))) + 'instance at %s>' % (_BUILTINS, address)) def test___repr___checker_allows_str(self): target = object() @@ -169,9 +180,10 @@ class ProxyTestBase(object): target = object() checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) + address = _fmt_address(target) self.assertEqual(repr(proxy), '<security proxied %s.object ' - 'instance at 0x%0x>' % (_BUILTINS, id(target))) + 'instance at %s>' % (_BUILTINS, address)) @_skip_if_not_Py2 def test___cmp___w_self(self): @@ -13,6 +13,7 @@ deps = zope.component zope.location zope.proxy + zope.testrunner commands = python setup.py test -q @@ -40,7 +41,7 @@ deps = [testenv:docs] basepython = - python2.6 + python3.3 commands = sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest |