Merge lp:~bellini666/storm/py3 into lp:storm

Proposed by Thiago Bellini
Status: Merged
Merge reported by: Colin Watson
Merged at revision: not available
Proposed branch: lp:~bellini666/storm/py3
Merge into: lp:storm
Diff against target: 3359 lines (+481/-675)
57 files modified
ez_setup.py (+0/-284)
setup.py (+20/-36)
storm/__init__.py (+3/-1)
storm/base.py (+3/-2)
storm/cache.py (+3/-2)
storm/cextensions.c (+61/-83)
storm/database.py (+11/-6)
storm/databases/__init__.py (+2/-1)
storm/databases/mysql.py (+2/-1)
storm/databases/postgres.py (+6/-4)
storm/databases/sqlite.py (+7/-4)
storm/event.py (+1/-0)
storm/exceptions.py (+20/-10)
storm/expr.py (+23/-17)
storm/info.py (+8/-3)
storm/properties.py (+5/-3)
storm/references.py (+11/-5)
storm/schema/patch.py (+6/-5)
storm/schema/schema.py (+4/-2)
storm/schema/sharding.py (+2/-1)
storm/sqlobject.py (+18/-14)
storm/store.py (+8/-6)
storm/tracer.py (+3/-1)
storm/twisted/testing.py (+1/-0)
storm/twisted/transact.py (+2/-1)
storm/tz.py (+41/-48)
storm/uri.py (+7/-2)
storm/variables.py (+27/-21)
storm/xid.py (+1/-0)
storm/zope/schema.py (+1/-0)
storm/zope/testing.py (+1/-1)
storm/zope/zstorm.py (+3/-1)
tests/cache.py (+6/-3)
tests/database.py (+3/-0)
tests/databases/base.py (+17/-13)
tests/databases/postgres.py (+6/-4)
tests/databases/proxy.py (+7/-5)
tests/databases/sqlite.py (+3/-2)
tests/event.py (+1/-0)
tests/expr.py (+10/-7)
tests/helper.py (+5/-1)
tests/info.py (+4/-2)
tests/mocker.py (+28/-20)
tests/properties.py (+4/-2)
tests/schema/patch.py (+6/-4)
tests/schema/schema.py (+2/-3)
tests/schema/sharding.py (+3/-1)
tests/sqlobject.py (+3/-2)
tests/store/base.py (+26/-21)
tests/store/postgres.py (+1/-0)
tests/tracer.py (+4/-2)
tests/uri.py (+1/-0)
tests/variables.py (+18/-14)
tests/wsgi.py (+5/-2)
tests/zope/adapters.py (+1/-0)
tests/zope/testing.py (+2/-1)
tests/zope/zstorm.py (+4/-1)
To merge this branch: bzr merge lp:~bellini666/storm/py3
Reviewer Review Type Date Requested Status
Stuart Bishop (community) Needs Fixing
Review via email: mp+325182@code.launchpad.net

Commit message

Make storm work with python2 and python3 using futurize and six.

Description of the change

Make storm work with python2 and python3 using futurize and six.

This branch has been tested with stoq <https://github.com/stoq/stoq> test suite, which covers a good amount of storm code using postgresql.

To post a comment you must log in.
lp:~bellini666/storm/py3 updated
494. By Thiago Bellini

Workaround virtual subclass checking in python3

The exception handling ignores virtual subclasses in python3

495. By Thiago Bellini

Update setup.py using the one from the debian package

Revision history for this message
Stuart Bishop (stub) wrote :
Download full text (4.4 KiB)

A critical part of landing this on trunk is a test suite working under both python2.7 and python3. I haven't succeeded here (using a fresh Xenial container).

The mechanical python2->python3 bits seem fine.

I'm in no way qualified to review storm/cextensions.c , so will be relying on the tests here unless I can get some alternative eyeballs to have a look.

I'm unsure about the changes from explicit large ints to plain ints (1L -> 1). I haven't looked closely into what the tests that needed this change were actually testing, and how this change can be valid under both py2 and py3.

I've setup a clean Xenial container, and setup a dev environment to run the test suite (we can only land this if the tests pass under both python2 and python3).

dev/ubuntu-deps needs updating (or an alternative mechanism for getting developer dependencies. Add these packages: python-future python3-future python-dev python3-dev python3-mysqldb python3-fixtures python3-psycopg2 python3-testresources python3-transaction python3-twisted python3-zope.component python3-zope.security python3-setuptools

'make check' uses the default python2, and is running 0 tests. Something has broken with test discovery, and this needs to be fixed.

'make check PYTHON=python3' uses python3, and is failing to build:

$ make check PYTHON=python3
STORM_CEXTENSIONS=0 python3 setup.py test
running test
running egg_info
writing dependency_links to storm.egg-info/dependency_links.txt
writing top-level names to storm.egg-info/top_level.txt
writing storm.egg-info/PKG-INFO
reading manifest file 'storm.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no files found matching 'ez_setup.py'
writing manifest file 'storm.egg-info/SOURCES.txt'
running build_ext
copying build/lib.linux-x86_64-3.5/storm/cextensions.cpython-35m-x86_64-linux-gnu.so -> storm
Traceback (most recent call last):
  File "setup.py", line 57, in <module>
    [Extension("storm.cextensions", ["storm/cextensions.c"])])
  File "/usr/lib/python3.5/distutils/core.py", line 148, in setup
    dist.run_commands()
  File "/usr/lib/python3.5/distutils/dist.py", line 955, in run_commands
    self.run_command(cmd)
  File "/usr/lib/python3.5/distutils/dist.py", line 974, in run_command
    cmd_obj.run()
  File "/usr/lib/python3/dist-packages/setuptools/command/test.py", line 159, in run
    self.with_project_on_sys_path(self.run_tests)
  File "/usr/lib/python3/dist-packages/setuptools/command/test.py", line 140, in with_project_on_sys_path
    func()
  File "/usr/lib/python3/dist-packages/setuptools/command/test.py", line 180, in run_tests
    testRunner=self._resolve_as_ep(self.test_runner),
  File "/usr/lib/python3.5/unittest/main.py", line 93, in __init__
    self.parseArgs(argv)
  File "/usr/lib/python3.5/unittest/main.py", line 123, in parseArgs
    self._do_discovery([])
  File "/usr/lib/python3.5/unittest/main.py", line 228, in _do_discovery
    self.test = loader.discover(self.start, self.pattern, self.top)
  File "/usr/lib/python3.5/unittest/loader.py", line 341, in discover
    tests = list(self._find_tests(start_dir, pattern))
  File "/usr/lib/python3.5/unittest/loader.py", line 398, in _find_tests
    full_pa...

Read more...

review: Needs Fixing
Revision history for this message
Ronaldo Maia (romaia) wrote :

I will assume this patch and will send a new version addressing this issues (and some other issues I found with cextensions).

Revision history for this message
Markus Kemmerling (markus-kemmerling) wrote :

Any news on this? I would really like to see a Python 3 compatible release of storm.

The branch worked for me after two additional fixes. The first one is related to Zope security proxies. For some reason I do not really understand I had to define a '__len__' method for result and reference sets to be able to pass security proxied sets to the 'list' or 'tuple' constructor (although iterating over the sets worked).

The second fix affects the C extensions which raised a 'TypeError' for compile methods that pass the 'join' parameter, e.g. 'storm.expr.compile_compound_oper()'. I am not a C programmer and never wrote a Python C extension. But after reading a few lines of the C API docs I found that the error arises from an invalid 'S' format character in the 'PyArg_ParseTupleAndKeywords()' call inside 'Compile__call__()'. Replacing the 'S' with an 'U' made the C extensions work for me:

--- cextensions.c.ORIG 2018-07-06 08:46:29.000000000 +0200
+++ cextensions.c 2018-07-05 14:37:02.000000000 +0200
@@ -1682,7 +1682,7 @@

     join = default_compile_join;

- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OSbb", kwlist,
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OUbb", kwlist,
                                      &expr, &state, &join, &raw, &token)) {
         return NULL;
     }

Revision history for this message
Colin Watson (cjwatson) wrote :

Thanks for doing this work.

Status report: I've been breaking this down into more reviewable pieces and sending small MPs for each (in some cases doing the work independently, and in some cases borrowing from this MP and giving credit where appropriate). I also switched to tox for running tests, which makes it much easier to run a suitable test matrix. There's still quite a bit to go, but I've got a fair amount merged already and I plan to continue.

Revision history for this message
Thiago Bellini (bellini666) wrote :

> Thanks for doing this work.
>
> Status report: I've been breaking this down into more reviewable pieces and
> sending small MPs for each (in some cases doing the work independently, and in
> some cases borrowing from this MP and giving credit where appropriate). I
> also switched to tox for running tests, which makes it much easier to run a
> suitable test matrix. There's still quite a bit to go, but I've got a fair
> amount merged already and I plan to continue.

I don't like the fact that the work I've done is being authored by someone else, and I'm loosing the right to say that I contributed to the project. Sends a very sad message to future contributors...

At least this project is finally seeing an update to python3.

Revision history for this message
Colin Watson (cjwatson) wrote :

Huh, wait, I'm not claiming credit for your work; where I've committed things with my authorship they have been actually different in substantive ways (e.g. for int/long handling I took more care to ensure that the tests were still meaningful on Python 2 while not being syntax errors on Python 3, and for print function conversion I added a bunch of __future__ imports to make it more robust. By contrast, the string exception fix I landed just now (admittedly a small change) I cherry-picked directly from your branch and the commit has your authorship on it. I expect there to be more of the latter. You will absolutely not lose the right to say that you contributed to the project, and I'm sorry for giving that impression.

If I could have fixed up this branch and landed it then I absolutely would have done, but it was just too much to digest, particularly given the poor testing situation described by Stuart. Since most of the comments made on this branch were over a year and a half ago, it seemed likely that you weren't going to have time to engage with them, and attempts by other people to do so also seemed to have stalled. There are also aspects of this branch that I'm not sure I agree with and may want to do differently. On the other hand there are certainly parts of your changes where you seem to know better than I and I expect to be trying to land your changes rather directly, such as the virtual subclass handling or the fleshing out of special object methods.

The approach I'm taking of breaking the branch down into small pieces, separating the large but essentially-mechanical changes from the ones that are non-trivial and require thought so that reviewers can skim-read the former and think hard about the latter, means that most of your work can actually land even if we choose to do some parts of it differently. I would very much encourage future contributors (including you, if you choose to contribute further) to take the approach of many small merge proposals rather than one large one.

Revision history for this message
Thiago Bellini (bellini666) wrote :

Regarding the main issue, ok, I understood your motivations.

I would just disagree with the many small merge proposals in this situation specifically. Here are my thoughts about that:

1) The code is actually broken in small pieces. Each change is in its own commit, there's not a single large commit doing the whole "python2 -> python3" migration.

2) Having said that, knowing that there's a dependency between each of those changes in a lot of the times, dividing them into small merge proposals would produce a lot of divergences, specially after code reviews.

3) Testing those would be a nightmare since to test with python3, I cannot just test the unicode migration without doing other one if that needs to be done in the same piece of code.

4) Because storm uses bzr instead of git, there would be no way to open a lot of merge proposals, each one for each subsequent commit, and as long as they were merged in sequence, it would be possible to mimic what you are trying to acomplish.

5) Lastly, the purpose of a merge proposal is to merge a "feature" which was developed in a branch containing a sequence of commits. There are features which can be broken into parts, but "migrate python2 to python3" IMO is something atomic.

But again, that is my opinion and maybe your and the other maintainers of the project will disagree with me. Either way I just wanted to point it out.

Revision history for this message
Colin Watson (cjwatson) wrote :

OK, I indeed do disagree with you on this and it seems worth explaining why. Point-by-point:

1) I understand that, indeed, but merge proposals are the unit of code review and 3000+-line merge proposals are typically viewed as rather indigestible. It is true that it's possible to go and look at the individual commits, but it's then difficult to comment on those individually. (Some of this is admittedly due to shortcomings in the Launchpad code review system, but you work with what you've got.) It also puts maintainers in a difficult position when they disagree with something near the start of your sequence of commits: what are they to do when they give you feedback and you don't respond for over 18 months, which is something that does happen from time to time (and indeed happened in this case)? They can't easily merge the rest of it and finish off the last bits in some other way, because you've linearised it all.

2) Lots of the changes aren't interdependent and can be proposed in parallel without causing this kind of problem. This is exactly what I've been doing. If you propose the non-interdependent changes in a form that can be tested separately, then you make reviewers' lives easier because they know they can merge the uncontroversial ones separately and reduce the overall size of what's left; that's much harder with a single linearised branch because later commits might well rely on earlier ones and not be cherry-pickable.

3) It's true that it's less easy to test the whole assembly until you're quite far along. On the other hand this merge proposal wasn't fully tested with Python 3 anyway since it fails tests in the way that Stuart pointed out in August 2017 (I proposed https://code.launchpad.net/~cjwatson/storm/zope-interface-class-decorators/+merge/368445 to fix the particular test failures they quoted), and so there may still be other problems that we haven't reached yet; I understand that you tested with stoq, which is useful, but our first concern is to ensure that our own test suite passes. And in practice for Python 3 porting one often starts by working through many well-understood standard changes, ensuring that each one continues to pass tests on Python 2, and then seeing what's left once the codebase is in a testable state. This strategy works well enough when filing many small merge proposals, since they're of a size where the feedback loop between proposer and reviewer can be reasonably short.

4) You can use prerequisite branches on merge proposals if you like. See my comment at the end of 1) for the practical problems caused by this, though.

5) This is a reasonable philosophical disagreement, but I do find my approach works better in practice. I've done quite a lot of other Python 3 porting work this way.

In any case, I'm certainly grateful for the work you've done on this and will explicitly credit you in the NEWS file regardless of the exact balance of specific commits that end up with your name on them, as well as others who've contributed here.

Revision history for this message
Colin Watson (cjwatson) wrote :

This is now effectively landed (with some commits from this branch, and some commits prepared separately) in Storm 0.21. Thanks!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed file 'ez_setup.py'
2--- ez_setup.py 2011-10-18 13:59:56 +0000
3+++ ez_setup.py 1970-01-01 00:00:00 +0000
4@@ -1,284 +0,0 @@
5-#!python
6-"""Bootstrap setuptools installation
7-
8-If you want to use setuptools in your package's setup.py, just include this
9-file in the same directory with it, and add this to the top of your setup.py::
10-
11- from ez_setup import use_setuptools
12- use_setuptools()
13-
14-If you want to require a specific version of setuptools, set a download
15-mirror, or use an alternate download directory, you can do so by supplying
16-the appropriate options to ``use_setuptools()``.
17-
18-This file can also be run as a script to install or upgrade setuptools.
19-"""
20-import sys
21-DEFAULT_VERSION = "0.6c11"
22-DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
23-
24-md5_data = {
25- 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
26- 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
27- 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
28- 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
29- 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
30- 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
31- 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
32- 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
33- 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
34- 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
35- 'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090',
36- 'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4',
37- 'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7',
38- 'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5',
39- 'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de',
40- 'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b',
41- 'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2',
42- 'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086',
43- 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
44- 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
45- 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
46- 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
47- 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
48- 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
49- 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
50- 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
51- 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
52- 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
53- 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
54- 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
55- 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
56- 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
57- 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
58- 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
59- 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
60- 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
61- 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
62- 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
63- 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
64- 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
65- 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
66- 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
67-}
68-
69-import sys, os
70-try: from hashlib import md5
71-except ImportError: from md5 import md5
72-
73-def _validate_md5(egg_name, data):
74- if egg_name in md5_data:
75- digest = md5(data).hexdigest()
76- if digest != md5_data[egg_name]:
77- print >>sys.stderr, (
78- "md5 validation of %s failed! (Possible download problem?)"
79- % egg_name
80- )
81- sys.exit(2)
82- return data
83-
84-def use_setuptools(
85- version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
86- download_delay=15
87-):
88- """Automatically find/download setuptools and make it available on sys.path
89-
90- `version` should be a valid setuptools version number that is available
91- as an egg for download under the `download_base` URL (which should end with
92- a '/'). `to_dir` is the directory where setuptools will be downloaded, if
93- it is not already available. If `download_delay` is specified, it should
94- be the number of seconds that will be paused before initiating a download,
95- should one be required. If an older version of setuptools is installed,
96- this routine will print a message to ``sys.stderr`` and raise SystemExit in
97- an attempt to abort the calling script.
98- """
99- was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
100- def do_download():
101- egg = download_setuptools(version, download_base, to_dir, download_delay)
102- sys.path.insert(0, egg)
103- import setuptools; setuptools.bootstrap_install_from = egg
104- try:
105- import pkg_resources
106- except ImportError:
107- return do_download()
108- try:
109- pkg_resources.require("setuptools>="+version); return
110- except pkg_resources.VersionConflict, e:
111- if was_imported:
112- print >>sys.stderr, (
113- "The required version of setuptools (>=%s) is not available, and\n"
114- "can't be installed while this script is running. Please install\n"
115- " a more recent version first, using 'easy_install -U setuptools'."
116- "\n\n(Currently using %r)"
117- ) % (version, e.args[0])
118- sys.exit(2)
119- except pkg_resources.DistributionNotFound:
120- pass
121-
122- del pkg_resources, sys.modules['pkg_resources'] # reload ok
123- return do_download()
124-
125-def download_setuptools(
126- version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
127- delay = 15
128-):
129- """Download setuptools from a specified location and return its filename
130-
131- `version` should be a valid setuptools version number that is available
132- as an egg for download under the `download_base` URL (which should end
133- with a '/'). `to_dir` is the directory where the egg will be downloaded.
134- `delay` is the number of seconds to pause before an actual download attempt.
135- """
136- import urllib2, shutil
137- egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
138- url = download_base + egg_name
139- saveto = os.path.join(to_dir, egg_name)
140- src = dst = None
141- if not os.path.exists(saveto): # Avoid repeated downloads
142- try:
143- from distutils import log
144- if delay:
145- log.warn("""
146----------------------------------------------------------------------------
147-This script requires setuptools version %s to run (even to display
148-help). I will attempt to download it for you (from
149-%s), but
150-you may need to enable firewall access for this script first.
151-I will start the download in %d seconds.
152-
153-(Note: if this machine does not have network access, please obtain the file
154-
155- %s
156-
157-and place it in this directory before rerunning this script.)
158----------------------------------------------------------------------------""",
159- version, download_base, delay, url
160- ); from time import sleep; sleep(delay)
161- log.warn("Downloading %s", url)
162- src = urllib2.urlopen(url)
163- # Read/write all in one block, so we don't create a corrupt file
164- # if the download is interrupted.
165- data = _validate_md5(egg_name, src.read())
166- dst = open(saveto,"wb"); dst.write(data)
167- finally:
168- if src: src.close()
169- if dst: dst.close()
170- return os.path.realpath(saveto)
171-
172-
173-
174-
175-
176-
177-
178-
179-
180-
181-
182-
183-
184-
185-
186-
187-
188-
189-
190-
191-
192-
193-
194-
195-
196-
197-
198-
199-
200-
201-
202-
203-
204-
205-
206-
207-def main(argv, version=DEFAULT_VERSION):
208- """Install or upgrade setuptools and EasyInstall"""
209- try:
210- import setuptools
211- except ImportError:
212- egg = None
213- try:
214- egg = download_setuptools(version, delay=0)
215- sys.path.insert(0,egg)
216- from setuptools.command.easy_install import main
217- return main(list(argv)+[egg]) # we're done here
218- finally:
219- if egg and os.path.exists(egg):
220- os.unlink(egg)
221- else:
222- if setuptools.__version__ == '0.0.1':
223- print >>sys.stderr, (
224- "You have an obsolete version of setuptools installed. Please\n"
225- "remove it from your system entirely before rerunning this script."
226- )
227- sys.exit(2)
228-
229- req = "setuptools>="+version
230- import pkg_resources
231- try:
232- pkg_resources.require(req)
233- except pkg_resources.VersionConflict:
234- try:
235- from setuptools.command.easy_install import main
236- except ImportError:
237- from easy_install import main
238- main(list(argv)+[download_setuptools(delay=0)])
239- sys.exit(0) # try to force an exit
240- else:
241- if argv:
242- from setuptools.command.easy_install import main
243- main(argv)
244- else:
245- print "Setuptools version",version,"or greater has been installed."
246- print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
247-
248-def update_md5(filenames):
249- """Update our built-in md5 registry"""
250-
251- import re
252-
253- for name in filenames:
254- base = os.path.basename(name)
255- f = open(name,'rb')
256- md5_data[base] = md5(f.read()).hexdigest()
257- f.close()
258-
259- data = [" %r: %r,\n" % it for it in md5_data.items()]
260- data.sort()
261- repl = "".join(data)
262-
263- import inspect
264- srcfile = inspect.getsourcefile(sys.modules[__name__])
265- f = open(srcfile, 'rb'); src = f.read(); f.close()
266-
267- match = re.search("\nmd5_data = {\n([^}]+)}", src)
268- if not match:
269- print >>sys.stderr, "Internal error!"
270- sys.exit(2)
271-
272- src = src[:match.start(1)] + repl + src[match.end(1):]
273- f = open(srcfile,'w')
274- f.write(src)
275- f.close()
276-
277-
278-if __name__=='__main__':
279- if len(sys.argv)>2 and sys.argv[1]=='--md5update':
280- update_md5(sys.argv[2:])
281- else:
282- main(sys.argv[1:])
283-
284-
285-
286-
287-
288-
289
290=== modified file 'setup.py'
291--- setup.py 2016-03-01 13:09:28 +0000
292+++ setup.py 2017-06-09 20:57:05 +0000
293@@ -2,10 +2,10 @@
294 import os
295 import re
296
297-import ez_setup
298-ez_setup.use_setuptools()
299-
300-from setuptools import setup, Extension, find_packages
301+try:
302+ from setuptools import setup, Extension
303+except ImportError:
304+ from distutils.core import setup, Extension
305
306
307 if os.path.isfile("MANIFEST"):
308@@ -19,12 +19,20 @@
309 open("storm/__init__.py").read()).group(1)
310
311
312+def find_packages():
313+ # implement a simple find_packages so we don't have to depend on
314+ # setuptools
315+ packages = []
316+ for directory, subdirectories, files in os.walk("storm"):
317+ if '__init__.py' in files:
318+ packages.append(directory.replace(os.sep, '.'))
319+ return packages
320+
321+
322 setup(
323 name="storm",
324 version=VERSION,
325- description=(
326- "Storm is an object-relational mapper (ORM) for Python "
327- "developed at Canonical."),
328+ description="Storm is an object-relational mapper (ORM) for Python developed at Canonical.",
329 author="Gustavo Niemeyer",
330 author_email="gustavo@niemeyer.net",
331 maintainer="Storm Developers",
332@@ -33,42 +41,18 @@
333 url="https://storm.canonical.com",
334 download_url="https://launchpad.net/storm/+download",
335 packages=find_packages(),
336+ zip_safe=False,
337+ include_package_data=True,
338 package_data={"": ["*.zcml"]},
339 classifiers=[
340 "Development Status :: 5 - Production/Stable",
341 "Intended Audience :: Developers",
342- ("License :: OSI Approved :: GNU Library or "
343- "Lesser General Public License (LGPL)"),
344+ "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
345 "Programming Language :: Python",
346 "Topic :: Database",
347 "Topic :: Database :: Front-Ends",
348 "Topic :: Software Development :: Libraries :: Python Modules",
349 ],
350 ext_modules=(BUILD_CEXTENSIONS and
351- [Extension("storm.cextensions", ["storm/cextensions.c"])]),
352- # The following options are specific to setuptools but ignored (with a
353- # warning) by distutils.
354- include_package_data=True,
355- zip_safe=False,
356- test_suite = "tests.find_tests",
357- tests_require=[
358- # Versions based on Lucid, where packaged.
359- "fixtures >= 0.3.5",
360- # pgbouncer (the Python module) is not yet packaged in Ubuntu.
361- "pgbouncer >= 0.0.7",
362- "psycopg2 >= 2.0.13",
363- "testresources >= 0.2.4",
364- "testtools >= 0.9.8",
365- # timeline is not yet packaged in Ubuntu.
366- "timeline >= 0.0.2",
367- "transaction >= 1.0.0",
368- "twisted >= 10.0.0",
369- "zope.component >= 3.8.0",
370- # zope.component 3.11.0 requires a version of zope.interface that no
371- # version of Ubuntu yet packages. The following rule exists for the
372- # sake of convenience rather than necessity, for the situation where
373- # zope.interface is installed via a package but zope.component is not.
374- "zope.component < 3.11.0",
375- "zope.security >= 3.7.2",
376- ],
377- )
378+ [Extension("storm.cextensions", ["storm/cextensions.c"])])
379+)
380
381=== modified file 'storm/__init__.py'
382--- storm/__init__.py 2013-06-28 09:56:58 +0000
383+++ storm/__init__.py 2017-06-09 20:57:05 +0000
384@@ -19,6 +19,8 @@
385 # along with this program. If not, see <http://www.gnu.org/licenses/>.
386 #
387
388+from builtins import str
389+from builtins import object
390 import os
391
392
393@@ -53,6 +55,6 @@
394 try:
395 from storm import cextensions
396 has_cextensions = True
397- except ImportError, e:
398+ except ImportError as e:
399 if "cextensions" not in str(e):
400 raise
401
402=== modified file 'storm/base.py'
403--- storm/base.py 2007-07-05 20:29:25 +0000
404+++ storm/base.py 2017-06-09 20:57:05 +0000
405@@ -18,18 +18,19 @@
406 # You should have received a copy of the GNU Lesser General Public License
407 # along with this program. If not, see <http://www.gnu.org/licenses/>.
408 #
409+from builtins import object
410 from storm.properties import PropertyPublisherMeta
411+from future.utils import with_metaclass
412
413
414 __all__ = ["Storm"]
415
416
417-class Storm(object):
418+class Storm(with_metaclass(PropertyPublisherMeta, object)):
419 """An optional base class for objects stored in a Storm Store.
420
421 It causes your subclasses to be associated with a Storm
422 PropertyRegistry. It is necessary to use this if you want to
423 specify References with strings.
424 """
425- __metaclass__ = PropertyPublisherMeta
426
427
428=== modified file 'storm/cache.py'
429--- storm/cache.py 2009-02-16 10:44:31 +0000
430+++ storm/cache.py 2017-06-09 20:57:05 +0000
431@@ -1,3 +1,4 @@
432+from builtins import object
433 import itertools
434
435
436@@ -140,8 +141,8 @@
437 objects, but no more than twice that number.
438 """
439 self._size = size
440- cache = itertools.islice(itertools.chain(self._new_cache.iteritems(),
441- self._old_cache.iteritems()),
442+ cache = itertools.islice(itertools.chain(iter(self._new_cache.items()),
443+ iter(self._old_cache.items())),
444 0, size)
445 self._new_cache = dict(cache)
446 self._old_cache.clear()
447
448=== modified file 'storm/cextensions.c'
449--- storm/cextensions.c 2012-05-31 04:14:32 +0000
450+++ storm/cextensions.c 2017-06-09 20:57:05 +0000
451@@ -23,14 +23,15 @@
452 #include <Python.h>
453 #include <structmember.h>
454
455-
456-#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
457-typedef int Py_ssize_t;
458-#define PY_SSIZE_T_MAX INT_MAX
459-#define PY_SSIZE_T_MIN INT_MIN
460+#if PY_VERSION_HEX >= 0x03000000
461+#define PyInt_FromLong PyLong_FromLong
462+#define PyText_AsString _PyUnicode_AsString
463+#define PyString_CheckExact(o) 0
464+#else
465+/* 2.x */
466+#define PyText_AsString PyString_AsString
467 #endif
468
469-
470 #define CATCH(error_value, expression) \
471 do { \
472 if ((expression) == error_value) {\
473@@ -46,57 +47,6 @@
474 Py_DECREF(tmp); \
475 } while(0)
476
477-
478-/* Python 2.4 does not include the PySet_* API, so provide a minimal
479- implementation for the calls we care about. */
480-#if PY_VERSION_HEX < 0x02050000 && !defined(PySet_GET_SIZE)
481-# define PySet_GET_SIZE(so) \
482- ((PyDictObject *)((PySetObject *)so)->data)->ma_used
483-static PyObject *
484-PySet_New(PyObject *p)
485-{
486- return PyObject_CallObject((PyObject *)&PySet_Type, NULL);
487-}
488-
489-static int
490-PySet_Add(PyObject *set, PyObject *key)
491-{
492- PyObject *dict;
493-
494- if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) {
495- PyErr_BadInternalCall();
496- return -1;
497- }
498- dict = ((PySetObject *)set)->data;
499- return PyDict_SetItem(dict, key, Py_True);
500-}
501-
502-static int
503-PySet_Discard(PyObject *set, PyObject *key)
504-{
505- PyObject *dict;
506- int result;
507-
508- if (!PyType_IsSubtype(set->ob_type, &PySet_Type)) {
509- PyErr_BadInternalCall();
510- return -1;
511- }
512- dict = ((PySetObject *)set)->data;
513- result = PyDict_DelItem(dict, key);
514- if (result == 0) {
515- /* key found and removed */
516- result = 1;
517- } else {
518- if (PyErr_ExceptionMatches(PyExc_KeyError)) {
519- /* key not found */
520- PyErr_Clear();
521- result = 0;
522- }
523- }
524- return result;
525-}
526-#endif
527-
528 static PyObject *Undef = NULL;
529 static PyObject *LazyValue = NULL;
530 static PyObject *raise_none_error = NULL;
531@@ -300,7 +250,7 @@
532 EventSystem_dealloc(EventSystemObject *self)
533 {
534 EventSystem_clear(self);
535- self->ob_type->tp_free((PyObject *)self);
536+ Py_TYPE(self)->tp_free((PyObject *)self);
537 }
538
539 static PyObject *
540@@ -521,9 +471,8 @@
541 };
542 #undef OFFSETOF
543
544-statichere PyTypeObject EventSystem_Type = {
545- PyObject_HEAD_INIT(NULL)
546- 0, /*ob_size*/
547+static PyTypeObject EventSystem_Type = {
548+ PyVarObject_HEAD_INIT(NULL, 0)
549 "storm.variables.EventSystem", /*tp_name*/
550 sizeof(EventSystemObject), /*tp_basicsize*/
551 0, /*tp_itemsize*/
552@@ -707,7 +656,7 @@
553 Variable_dealloc(VariableObject *self)
554 {
555 Variable_clear(self);
556- self->ob_type->tp_free((PyObject *)self);
557+ Py_TYPE(self)->tp_free((PyObject *)self);
558 }
559
560 static PyObject *
561@@ -1065,7 +1014,7 @@
562
563 /* variable = self.__class__.__new__(self.__class__) */
564 noargs = PyTuple_New(0);
565- CATCH(NULL, variable = self->ob_type->tp_new(self->ob_type, noargs, NULL));
566+ CATCH(NULL, variable = Py_TYPE(self)->tp_new(Py_TYPE(self), noargs, NULL));
567
568 /* variable.set_state(self.get_state()) */
569 CATCH(NULL,
570@@ -1116,9 +1065,8 @@
571 };
572 #undef OFFSETOF
573
574-statichere PyTypeObject Variable_Type = {
575- PyObject_HEAD_INIT(NULL)
576- 0, /*ob_size*/
577+static PyTypeObject Variable_Type = {
578+ PyVarObject_HEAD_INIT(NULL, 0)
579 "storm.variables.Variable", /*tp_name*/
580 sizeof(VariableObject), /*tp_basicsize*/
581 0, /*tp_itemsize*/
582@@ -1267,7 +1215,7 @@
583 Compile_dealloc(CompileObject *self)
584 {
585 Compile_clear(self);
586- self->ob_type->tp_free((PyObject *)self);
587+ Py_TYPE(self)->tp_free((PyObject *)self);
588 }
589
590 static PyObject *
591@@ -1434,13 +1382,13 @@
592 return NULL;
593 }
594
595-staticforward PyTypeObject Compile_Type;
596+static PyTypeObject Compile_Type;
597
598 static PyObject *
599 Compile_create_child(CompileObject *self, PyObject *args)
600 {
601 /* return self.__class__(self) */
602- return PyObject_CallFunctionObjArgs((PyObject *)self->ob_type, self, NULL);
603+ return PyObject_CallFunctionObjArgs((PyObject *)Py_TYPE(self), self, NULL);
604 }
605
606 static PyObject *
607@@ -1538,7 +1486,7 @@
608 if (repr) {
609 PyErr_Format(CompileError,
610 "Don't know how to compile type %s of %s",
611- expr->ob_type->tp_name, PyString_AS_STRING(repr));
612+ expr->ob_type->tp_name, PyText_AsString(repr));
613 Py_DECREF(repr);
614 }
615 goto error;
616@@ -1557,7 +1505,7 @@
617 state, NULL));
618
619 /* if inner_precedence < outer_precedence: */
620- if (PyObject_Compare(inner_precedence, outer_precedence) == -1) {
621+ if (PyObject_RichCompareBool(inner_precedence, outer_precedence, Py_LT)) {
622 PyObject *args, *tmp;
623
624 if (PyErr_Occurred())
625@@ -1780,9 +1728,8 @@
626 };
627 #undef OFFSETOF
628
629-statichere PyTypeObject Compile_Type = {
630- PyObject_HEAD_INIT(NULL)
631- 0, /*ob_size*/
632+static PyTypeObject Compile_Type = {
633+ PyVarObject_HEAD_INIT(NULL, 0)
634 "storm.variables.Compile", /*tp_name*/
635 sizeof(CompileObject), /*tp_basicsize*/
636 0, /*tp_itemsize*/
637@@ -2071,9 +2018,8 @@
638 {NULL}
639 };
640
641-statichere PyTypeObject ObjectInfo_Type = {
642- PyObject_HEAD_INIT(NULL)
643- 0, /*ob_size*/
644+static PyTypeObject ObjectInfo_Type = {
645+ PyVarObject_HEAD_INIT(NULL, 0)
646 "storm.info.ObjectInfo", /*tp_name*/
647 sizeof(ObjectInfoObject), /*tp_basicsize*/
648 0, /*tp_itemsize*/
649@@ -2176,11 +2122,9 @@
650 return PyType_Ready(type);
651 }
652
653-DL_EXPORT(void)
654-initcextensions(void)
655+static int
656+do_init(PyObject *module)
657 {
658- PyObject *module;
659-
660 prepare_type(&EventSystem_Type);
661 prepare_type(&Compile_Type);
662 ObjectInfo_Type.tp_base = &PyDict_Type;
663@@ -2188,7 +2132,6 @@
664 prepare_type(&ObjectInfo_Type);
665 prepare_type(&Variable_Type);
666
667- module = Py_InitModule3("cextensions", cextensions_methods, "");
668 Py_INCREF(&Variable_Type);
669
670 #define REGISTER_TYPE(name) \
671@@ -2201,7 +2144,42 @@
672 REGISTER_TYPE(ObjectInfo);
673 REGISTER_TYPE(Compile);
674 REGISTER_TYPE(EventSystem);
675-}
676+ return 0;
677+}
678+
679+#if PY_VERSION_HEX < 0x03000000
680+DL_EXPORT(void)
681+initcextensions(void)
682+{
683+ PyObject *module;
684+
685+ module = Py_InitModule3("cextensions", cextensions_methods, "");
686+ do_init(module);
687+}
688+#else
689+static struct PyModuleDef cextensionsmodule = {
690+ PyModuleDef_HEAD_INIT,
691+ "cextensions",
692+ NULL,
693+ -1,
694+ cextensions_methods,
695+ NULL,
696+ NULL,
697+ NULL,
698+ NULL
699+};
700+
701+PyMODINIT_FUNC
702+PyInit_cextensions(void)
703+{
704+ PyObject *module = PyModule_Create(&cextensionsmodule);
705+ if (module == NULL)
706+ return NULL;
707+ do_init(module);
708+ return module;
709+}
710+#endif
711+
712
713 /* vim:ts=4:sw=4:et
714 */
715
716=== modified file 'storm/database.py'
717--- storm/database.py 2015-06-15 13:42:07 +0000
718+++ storm/database.py 2017-06-09 20:57:05 +0000
719@@ -25,6 +25,11 @@
720 supported in modules in L{storm.databases}.
721 """
722
723+from builtins import str
724+from builtins import range
725+from builtins import object
726+import six
727+
728 from storm.expr import Expr, State, compile
729 # Circular import: imported at the end of the module.
730 # from storm.tracer import trace
731@@ -318,7 +323,7 @@
732 self._raw_connection.tpc_rollback()
733 else:
734 self._raw_connection.rollback()
735- except Error, exc:
736+ except Error as exc:
737 if self.is_disconnection_error(exc):
738 self._raw_connection = None
739 self._state = STATE_RECONNECT
740@@ -386,7 +391,7 @@
741 """Complete the statement execution, along with result reports."""
742 try:
743 self._check_disconnect(raw_cursor.execute, *args)
744- except Exception, error:
745+ except Exception as error:
746 self._check_disconnect(
747 trace, "connection_raw_execute_error", self, raw_cursor,
748 statement, params or (), error)
749@@ -402,7 +407,7 @@
750 self._check_disconnect(
751 trace, "connection_raw_execute", self, raw_cursor,
752 statement, params or ())
753- except Exception, error:
754+ except Exception as error:
755 self._check_disconnect(
756 trace, "connection_raw_execute_error", self, raw_cursor,
757 statement, params or (), error)
758@@ -423,7 +428,7 @@
759 elif self._state == STATE_RECONNECT:
760 try:
761 self._raw_connection = self._database.raw_connect()
762- except DatabaseError, exc:
763+ except DatabaseError as exc:
764 self._state = STATE_DISCONNECTED
765 self._raw_connection = None
766 raise DisconnectionError(str(exc))
767@@ -452,7 +457,7 @@
768 'extra_disconnection_errors', ())
769 try:
770 return function(*args, **kwargs)
771- except Exception, exc:
772+ except Exception as exc:
773 if self.is_disconnection_error(exc, extra_disconnection_errors):
774 self._state = STATE_DISCONNECTED
775 self._raw_connection = None
776@@ -544,7 +549,7 @@
777 - "anything:..." Where 'anything' has previously been registered
778 with L{register_scheme}.
779 """
780- if isinstance(uri, basestring):
781+ if isinstance(uri, six.string_types):
782 uri = URI(uri)
783 if uri.scheme in _database_schemes:
784 factory = _database_schemes[uri.scheme]
785
786=== modified file 'storm/databases/__init__.py'
787--- storm/databases/__init__.py 2007-08-07 18:36:04 +0000
788+++ storm/databases/__init__.py 2017-06-09 20:57:05 +0000
789@@ -20,6 +20,7 @@
790 #
791
792
793+from builtins import object
794 class Dummy(object):
795 """Magic "infectious" class.
796
797@@ -36,7 +37,7 @@
798 def __add__(self, other):
799 return self
800
801- def __nonzero__(self):
802+ def __bool__(self):
803 return False
804
805 dummy = Dummy()
806
807=== modified file 'storm/databases/mysql.py'
808--- storm/databases/mysql.py 2015-06-15 12:02:12 +0000
809+++ storm/databases/mysql.py 2017-06-09 20:57:05 +0000
810@@ -18,6 +18,7 @@
811 # You should have received a copy of the GNU Lesser General Public License
812 # along with this program. If not, see <http://www.gnu.org/licenses/>.
813 #
814+from builtins import str
815 from datetime import time, timedelta
816 from array import array
817 import sys
818@@ -48,7 +49,7 @@
819 @compile.when(Select)
820 def compile_select_mysql(compile, select, state):
821 if select.offset is not Undef and select.limit is Undef:
822- select.limit = sys.maxint
823+ select.limit = sys.maxsize
824 return compile_select(compile, select, state)
825
826 @compile.when(SQLToken)
827
828=== modified file 'storm/databases/postgres.py'
829--- storm/databases/postgres.py 2016-04-28 11:57:12 +0000
830+++ storm/databases/postgres.py 2017-06-09 20:57:05 +0000
831@@ -19,6 +19,8 @@
832 # along with this program. If not, see <http://www.gnu.org/licenses/>.
833 #
834
835+from builtins import zip, str
836+import six
837 from datetime import datetime, date, time, timedelta
838 import json
839
840@@ -310,7 +312,7 @@
841 Like L{Connection.raw_execute}, but encode the statement to
842 UTF-8 if it is unicode.
843 """
844- if type(statement) is unicode:
845+ if type(statement) is six.text_type and six.PY2:
846 # psycopg breaks with unicode statements.
847 statement = statement.encode("UTF-8")
848 return Connection.raw_execute(self, statement, params)
849@@ -326,9 +328,9 @@
850 param = param.get(to_db=True)
851 if isinstance(param, (datetime, date, time, timedelta)):
852 yield str(param)
853- elif isinstance(param, unicode):
854+ elif isinstance(param, six.text_type) and six.PY2:
855 yield param.encode("UTF-8")
856- elif isinstance(param, str):
857+ elif isinstance(param, six.binary_type):
858 yield psycopg2.Binary(param)
859 else:
860 yield param
861@@ -484,7 +486,7 @@
862 __slots__ = ()
863
864 def _loads(self, value):
865- if isinstance(value, str):
866+ if isinstance(value, six.binary_type):
867 # psycopg versions < 2.5 don't automatically convert JSON columns
868 # to python objects, they return a string.
869 #
870
871=== modified file 'storm/databases/sqlite.py'
872--- storm/databases/sqlite.py 2015-06-15 12:02:12 +0000
873+++ storm/databases/sqlite.py 2017-06-09 20:57:05 +0000
874@@ -18,6 +18,8 @@
875 # You should have received a copy of the GNU Lesser General Public License
876 # along with this program. If not, see <http://www.gnu.org/licenses/>.
877 #
878+from builtins import str
879+import six
880 from datetime import datetime, date, time, timedelta
881 from time import sleep, time as now
882 import sys
883@@ -41,6 +43,7 @@
884
885
886 install_exceptions(sqlite)
887+buffer = buffer if six.PY2 else memoryview
888
889
890 compile = compile.create_child()
891@@ -51,9 +54,9 @@
892 if sys.maxsize > 2**32:
893 # On 64-bit platforms sqlite doesn't like maxint as LIMIT. See also
894 # https://lists.ubuntu.com/archives/storm/2013-June/001492.html
895- select.limit = sys.maxint - 1
896+ select.limit = sys.maxsize - 1
897 else:
898- select.limit = sys.maxint
899+ select.limit = sys.maxsize
900 statement = compile_select(compile, select, state)
901 if state.context is SELECT:
902 # SQLite breaks with (SELECT ...) UNION (SELECT ...), so we
903@@ -119,7 +122,7 @@
904 param = param.get(to_db=True)
905 if isinstance(param, (datetime, date, time, timedelta)):
906 yield str(param)
907- elif isinstance(param, str):
908+ elif isinstance(param, six.binary_type):
909 yield buffer(param)
910 else:
911 yield param
912@@ -157,7 +160,7 @@
913 while True:
914 try:
915 return Connection.raw_execute(self, statement, params)
916- except sqlite.OperationalError, e:
917+ except sqlite.OperationalError as e:
918 if str(e) != "database is locked":
919 raise
920 elif now() - started < self._database._timeout:
921
922=== modified file 'storm/event.py'
923--- storm/event.py 2008-06-18 21:51:12 +0000
924+++ storm/event.py 2017-06-09 20:57:05 +0000
925@@ -18,6 +18,7 @@
926 # You should have received a copy of the GNU Lesser General Public License
927 # along with this program. If not, see <http://www.gnu.org/licenses/>.
928 #
929+from builtins import object
930 import weakref
931
932 from storm import has_cextensions
933
934=== modified file 'storm/exceptions.py'
935--- storm/exceptions.py 2015-11-23 15:03:53 +0000
936+++ storm/exceptions.py 2017-06-09 20:57:05 +0000
937@@ -19,11 +19,13 @@
938 # along with this program. If not, see <http://www.gnu.org/licenses/>.
939 #
940 from abc import ABCMeta
941+import six
942 import types
943-
944-
945-class StormError(Exception):
946- __metaclass__ = ABCMeta
947+from future.utils import with_metaclass
948+
949+
950+class StormError(with_metaclass(ABCMeta, Exception)):
951+ pass
952
953
954 class CompileError(StormError):
955@@ -128,7 +130,7 @@
956 self.message = message
957
958 def __str__(self):
959- return ', '.join(
960+ return ', '.join(
961 [repr(element) for element in
962 (self.message, self.statement, self.params)
963 if element is not None])
964@@ -144,8 +146,16 @@
965 DataError, NotSupportedError, InterfaceError):
966 module_exception = getattr(module, exception.__name__, None)
967 if (module_exception is not None and
968- isinstance(module_exception, (type, types.ClassType))):
969- # XXX This may need to be revisited when porting to Python 3 if
970- # virtual subclasses are still ignored for exception handling
971- # (https://bugs.python.org/issue12029).
972- exception.register(module_exception)
973+ isinstance(module_exception, type)):
974+ if six.PY2:
975+ exception.register(module_exception)
976+ else:
977+ # XXX virtual subclasses are still ignored for exception handling
978+ # (https://bugs.python.org/issue12029).
979+ try:
980+ module_exception.__bases__ += (exception, )
981+ except TypeError:
982+ tmp_exc = type(module_exception.__name__, (module_exception, ), {})
983+ setattr(module, module_exception.__name__, tmp_exc)
984+ module_exception = getattr(module, exception.__name__)
985+ module_exception.__bases__ += (exception, )
986
987=== modified file 'storm/expr.py'
988--- storm/expr.py 2016-05-13 18:55:24 +0000
989+++ storm/expr.py 2017-06-09 20:57:05 +0000
990@@ -18,6 +18,8 @@
991 # You should have received a copy of the GNU Lesser General Public License
992 # along with this program. If not, see <http://www.gnu.org/licenses/>.
993 #
994+from builtins import zip, range, object
995+import six
996 from decimal import Decimal
997 from datetime import datetime, date, time, timedelta
998 from weakref import WeakKeyDictionary
999@@ -164,10 +166,10 @@
1000 expr_type = type(expr)
1001
1002 if (expr_type is SQLRaw or
1003- raw and (expr_type is str or expr_type is unicode)):
1004+ raw and (expr_type is six.binary_type or expr_type is six.text_type)):
1005 return expr
1006
1007- if token and (expr_type is str or expr_type is unicode):
1008+ if token and (expr_type is six.binary_type or expr_type is six.text_type):
1009 expr = SQLToken(expr)
1010
1011 if state is None:
1012@@ -178,15 +180,15 @@
1013 compiled = []
1014 for subexpr in expr:
1015 subexpr_type = type(subexpr)
1016- if subexpr_type is SQLRaw or raw and (subexpr_type is str or
1017- subexpr_type is unicode):
1018+ if subexpr_type is SQLRaw or raw and (subexpr_type is six.binary_type or
1019+ subexpr_type is six.text_type):
1020 statement = subexpr
1021 elif subexpr_type is tuple or subexpr_type is list:
1022 state.precedence = outer_precedence
1023 statement = self(subexpr, state, join, raw, token)
1024 else:
1025- if token and (subexpr_type is unicode or
1026- subexpr_type is str):
1027+ if token and (subexpr_type is six.text_type or
1028+ subexpr_type is six.binary_type):
1029 subexpr = SQLToken(subexpr)
1030 statement = self._compile_single(subexpr, state,
1031 outer_precedence)
1032@@ -216,7 +218,7 @@
1033 " return match" %
1034 (",".join("_%d" % i for i in range(len(state.parameters))),
1035 source))
1036- exec code in namespace
1037+ exec(code, namespace)
1038 return namespace['closure'](state.parameters, bool)
1039
1040
1041@@ -303,17 +305,17 @@
1042 # --------------------------------------------------------------------
1043 # Builtin type support
1044
1045-@compile.when(str)
1046+@compile.when(six.binary_type)
1047 def compile_str(compile, expr, state):
1048 state.parameters.append(RawStrVariable(expr))
1049 return "?"
1050
1051-@compile.when(unicode)
1052+@compile.when(six.text_type)
1053 def compile_unicode(compile, expr, state):
1054 state.parameters.append(UnicodeVariable(expr))
1055 return "?"
1056
1057-@compile.when(int, long)
1058+@compile.when(*six.integer_types)
1059 def compile_int(compile, expr, state):
1060 state.parameters.append(IntVariable(expr))
1061 return "?"
1062@@ -358,7 +360,7 @@
1063 return "NULL"
1064
1065
1066-@compile_python.when(str, unicode, int, long, float, type(None))
1067+@compile_python.when(float, type(None), *(six.string_types + six.integer_types))
1068 def compile_python_builtin(compile, expr, state):
1069 return repr(expr)
1070
1071@@ -406,6 +408,7 @@
1072
1073 class Comparable(object):
1074 __slots__ = ()
1075+ __hash__ = object.__hash__
1076
1077 def __eq__(self, other):
1078 if other is not None and not isinstance(other, (Expr, Variable)):
1079@@ -477,6 +480,9 @@
1080 other = getattr(self, "variable_factory", Variable)(value=other)
1081 return Div(self, other)
1082
1083+ __floordiv__ = __div__
1084+ __truediv__ = __div__
1085+
1086 def __mod__(self, other):
1087 if not isinstance(other, (Expr, Variable)):
1088 other = getattr(self, "variable_factory", Variable)(value=other)
1089@@ -508,19 +514,19 @@
1090 return Upper(self)
1091
1092 def startswith(self, prefix):
1093- if not isinstance(prefix, unicode):
1094+ if not isinstance(prefix, six.text_type):
1095 raise ExprError("Expected unicode argument, got %r" % type(prefix))
1096 pattern = prefix.translate(like_escape) + u"%"
1097 return Like(self, pattern, u"!")
1098
1099 def endswith(self, suffix):
1100- if not isinstance(suffix, unicode):
1101+ if not isinstance(suffix, six.text_type):
1102 raise ExprError("Expected unicode argument, got %r" % type(suffix))
1103 pattern = u"%" + suffix.translate(like_escape)
1104 return Like(self, pattern, u"!")
1105
1106 def contains_string(self, substring):
1107- if not isinstance(substring, unicode):
1108+ if not isinstance(substring, six.text_type):
1109 raise ExprError("Expected unicode argument, got %r" % type(substring))
1110 pattern = u"%" + substring.translate(like_escape) + u"%"
1111 return Like(self, pattern, u"!")
1112@@ -736,7 +742,7 @@
1113 state.context = EXPR
1114 values = insert.values
1115 if values is Undef:
1116- values = [tuple(insert.map.itervalues())]
1117+ values = [tuple(insert.map.values())]
1118 if isinstance(values, Expr):
1119 compiled_values = compile(values, state)
1120 else:
1121@@ -1414,7 +1420,7 @@
1122 # --------------------------------------------------------------------
1123 # Plain SQL expressions.
1124
1125-class SQLRaw(str):
1126+class SQLRaw(six.text_type):
1127 """Subtype to mark a string as something that shouldn't be compiled.
1128
1129 This is handled internally by the compiler.
1130@@ -1422,7 +1428,7 @@
1131 __slots__ = ()
1132
1133
1134-class SQLToken(str):
1135+class SQLToken(six.text_type):
1136 """Marker for strings that should be considered as a single SQL token.
1137
1138 These strings will be quoted, when needed.
1139
1140=== modified file 'storm/info.py'
1141--- storm/info.py 2011-08-14 08:55:15 +0000
1142+++ storm/info.py 2017-06-09 20:57:05 +0000
1143@@ -18,8 +18,11 @@
1144 # You should have received a copy of the GNU Lesser General Public License
1145 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1146 #
1147+from builtins import object
1148 from weakref import ref
1149
1150+import six
1151+
1152 from storm.exceptions import ClassInfoError
1153 from storm.expr import Column, Desc, TABLE
1154 from storm.expr import compile, Table
1155@@ -73,7 +76,7 @@
1156
1157 self.cls = cls
1158
1159- if isinstance(self.table, basestring):
1160+ if isinstance(self.table, six.string_types):
1161 self.table = Table(self.table)
1162
1163 pairs = []
1164@@ -131,7 +134,7 @@
1165 __order__ = (__order__,)
1166 self.default_order = []
1167 for item in __order__:
1168- if isinstance(item, basestring):
1169+ if isinstance(item, six.string_types):
1170 if item.startswith("-"):
1171 prop = Desc(getattr(cls, item[1:]))
1172 else:
1173@@ -146,6 +149,8 @@
1174 def __ne__(self, other):
1175 return self is not other
1176
1177+ __hash__ = object.__hash__
1178+
1179
1180 class ObjectInfo(dict):
1181
1182@@ -192,7 +197,7 @@
1183 self.event.emit("object-deleted")
1184
1185 def checkpoint(self):
1186- for variable in self.variables.itervalues():
1187+ for variable in self.variables.values():
1188 variable.checkpoint()
1189
1190
1191
1192=== modified file 'storm/properties.py'
1193--- storm/properties.py 2011-02-28 21:16:29 +0000
1194+++ storm/properties.py 2017-06-09 20:57:05 +0000
1195@@ -18,6 +18,8 @@
1196 # You should have received a copy of the GNU Lesser General Public License
1197 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1198 #
1199+from builtins import zip
1200+from builtins import object
1201 from bisect import insort_left, bisect_left
1202 import weakref
1203 import sys
1204@@ -76,7 +78,7 @@
1205 def _detect_attr_name(self, used_cls):
1206 self_id = id(self)
1207 for cls in used_cls.__mro__:
1208- for attr, prop in cls.__dict__.items():
1209+ for attr, prop in list(cls.__dict__.items()):
1210 if id(prop) == self_id:
1211 return attr
1212 raise RuntimeError("Property used in an unknown class")
1213@@ -218,7 +220,7 @@
1214
1215 def __init__(self, name=None, primary=False, **kwargs):
1216 set_map = dict(kwargs.pop("map"))
1217- get_map = dict((value, key) for key, value in set_map.items())
1218+ get_map = dict((value, key) for key, value in list(set_map.items()))
1219 if "set_map" in kwargs:
1220 set_map = dict(kwargs.pop("set_map"))
1221
1222@@ -260,7 +262,7 @@
1223 i += 1
1224 else:
1225 namespace_parts = ("." + namespace).split(".")
1226- best_path_info = (0, sys.maxint)
1227+ best_path_info = (0, sys.maxsize)
1228 while i < l and self._properties[i][0].startswith(key):
1229 path, prop_ref = self._properties[i]
1230 prop = prop_ref()
1231
1232=== modified file 'storm/references.py'
1233--- storm/references.py 2013-06-28 08:13:08 +0000
1234+++ storm/references.py 2017-06-09 20:57:05 +0000
1235@@ -18,8 +18,12 @@
1236 # You should have received a copy of the GNU Lesser General Public License
1237 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1238 #
1239+from builtins import zip
1240+from builtins import object
1241 import weakref
1242
1243+import six
1244+
1245 from storm.exceptions import (
1246 ClassInfoError, FeatureError, NoStoreError, WrongStoreError)
1247 from storm.store import Store, get_where_for_args, LostObjectError
1248@@ -206,6 +210,8 @@
1249 def __ne__(self, other):
1250 return Not(self == other)
1251
1252+ __hash__ = object.__hash__
1253+
1254
1255 class ReferenceSet(object):
1256
1257@@ -621,8 +627,8 @@
1258 if setting:
1259 local_vars = local_info.variables
1260 remote_vars = remote_info.variables
1261- pairs = zip(self._get_local_columns(local.__class__),
1262- self.remote_key)
1263+ pairs = list(zip(self._get_local_columns(local.__class__),
1264+ self.remote_key))
1265 if self.on_remote:
1266 local_has_changed = False
1267 for local_column, remote_column in pairs:
1268@@ -910,7 +916,7 @@
1269 def resolve_one(self, property):
1270 if type(property) is tuple:
1271 return self.resolve(property)
1272- elif isinstance(property, basestring):
1273+ elif isinstance(property, six.string_types):
1274 return self._resolve_string(property)
1275 elif isinstance(property, SuffixExpr):
1276 # XXX This covers cases like order_by=Desc("Bar.id"), see #620369.
1277@@ -938,14 +944,14 @@
1278
1279 def _find_descriptor_class(used_cls, descr):
1280 for cls in used_cls.__mro__:
1281- for attr, _descr in cls.__dict__.iteritems():
1282+ for attr, _descr in cls.__dict__.items():
1283 if _descr is descr:
1284 return cls
1285 raise RuntimeError("Reference used in an unknown class")
1286
1287 def _find_descriptor_obj(used_cls, descr):
1288 for cls in used_cls.__mro__:
1289- for attr, _descr in cls.__dict__.iteritems():
1290+ for attr, _descr in cls.__dict__.items():
1291 if _descr is descr:
1292 return getattr(cls, attr)
1293 raise RuntimeError("Reference used in an unknown class")
1294
1295=== modified file 'storm/schema/patch.py'
1296--- storm/schema/patch.py 2014-12-12 11:14:47 +0000
1297+++ storm/schema/patch.py 2017-06-09 20:57:05 +0000
1298@@ -34,6 +34,9 @@
1299 'patch' table in the given L{Store}, and it won't be applied again.
1300 """
1301
1302+from future.utils import raise_
1303+from builtins import str
1304+from builtins import object
1305 import sys
1306 import os
1307 import re
1308@@ -118,10 +121,8 @@
1309 except:
1310 type, value, traceback = sys.exc_info()
1311 patch_repr = getattr(module, "__file__", version)
1312- raise BadPatchError, \
1313- "Patch %s failed: %s: %s" % \
1314- (patch_repr, type.__name__, str(value)), \
1315- traceback
1316+ msg = "Patch %s failed: %s: %s" % (patch_repr, type.__name__, str(value))
1317+ raise_(BadPatchError, msg, traceback)
1318 self._committer.commit()
1319
1320 def apply_all(self):
1321@@ -244,7 +245,7 @@
1322 patch_directory = self._get_patch_directory()
1323 filenames = os.listdir(patch_directory)
1324 matches = [(format.match(fn), fn) for fn in filenames]
1325- matches = sorted(filter(lambda x: x[0], matches),
1326+ matches = sorted([x for x in matches if x[0]],
1327 key=lambda x: int(x[0].group(1)))
1328 return [int(match.group(1)) for match, filename in matches]
1329
1330
1331=== modified file 'storm/schema/schema.py'
1332--- storm/schema/schema.py 2015-01-26 09:57:25 +0000
1333+++ storm/schema/schema.py 2017-06-09 20:57:05 +0000
1334@@ -42,7 +42,9 @@
1335 where patch_module is a Python module containing database patches used to
1336 upgrade the schema over time.
1337 """
1338+from __future__ import print_function
1339
1340+from builtins import object
1341 import types
1342
1343 from storm.locals import StormError
1344@@ -96,7 +98,7 @@
1345 try:
1346 store.execute(statement)
1347 except Exception:
1348- print "Error running %s" % statement
1349+ print("Error running %s" % statement)
1350 raise
1351 if self._autocommit:
1352 store.commit()
1353@@ -172,7 +174,7 @@
1354 except SchemaMissingError:
1355 # No schema at all. Create it from the ground.
1356 self.create(store)
1357- except UnappliedPatchesError, error:
1358+ except UnappliedPatchesError as error:
1359 patch_applier.check_unknown()
1360 for version in error.unapplied_versions:
1361 self.advance(store, version)
1362
1363=== modified file 'storm/schema/sharding.py'
1364--- storm/schema/sharding.py 2014-12-28 11:51:53 +0000
1365+++ storm/schema/sharding.py 2017-06-09 20:57:05 +0000
1366@@ -41,6 +41,7 @@
1367 be at the same patch level. See L{storm.schema.patch.PatchSet}.
1368 """
1369
1370+from builtins import object
1371 from storm.schema.schema import SchemaMissingError, UnappliedPatchesError
1372
1373
1374@@ -101,7 +102,7 @@
1375 schema.check(store)
1376 except SchemaMissingError:
1377 schema.create(store)
1378- except UnappliedPatchesError, error:
1379+ except UnappliedPatchesError as error:
1380 if not unapplied_versions:
1381 unapplied_versions = error.unapplied_versions
1382 elif unapplied_versions != error.unapplied_versions:
1383
1384=== modified file 'storm/sqlobject.py'
1385--- storm/sqlobject.py 2011-10-17 15:59:25 +0000
1386+++ storm/sqlobject.py 2017-06-09 20:57:05 +0000
1387@@ -23,6 +23,8 @@
1388 L{SQLObjectBase} is the central point of compatibility.
1389 """
1390
1391+from builtins import object
1392+import six
1393 import re
1394 import warnings
1395
1396@@ -40,6 +42,7 @@
1397 compare_columns)
1398 from storm.tz import tzutc
1399 from storm import Undef
1400+from future.utils import with_metaclass
1401
1402
1403 __all__ = [
1404@@ -162,7 +165,7 @@
1405 dict["__storm_table__"] = table_name
1406
1407 attr_to_prop = {}
1408- for attr, prop in dict.items():
1409+ for attr, prop in list(dict.items()):
1410 attr_to_prop[attr] = attr
1411 if isinstance(prop, ForeignKey):
1412 db_name = prop.kwargs.get("dbName", attr)
1413@@ -185,7 +188,7 @@
1414 if obj is None:
1415 raise SQLObjectNotFound
1416 return obj
1417- func.func_name = method_name
1418+ func.__name__ = method_name
1419 dict[method_name] = classmethod(func)
1420 elif isinstance(prop, SQLMultipleJoin):
1421 # Generate addFoo/removeFoo names.
1422@@ -205,7 +208,9 @@
1423
1424
1425 id_type = dict.setdefault("_idType", int)
1426- id_cls = {int: Int, str: RawStr, unicode: AutoUnicode}[id_type]
1427+ id_cls = {int: Int,
1428+ six.binary_type: RawStr,
1429+ six.text_type: AutoUnicode}[id_type]
1430 dict["id"] = id_cls(id_name, primary=True, default=AutoReload)
1431 attr_to_prop[id_name] = "id"
1432
1433@@ -222,7 +227,7 @@
1434 property_registry.add_property(obj, getattr(obj, "id"),
1435 "<table %s>" % table_name)
1436
1437- for fake_name, real_name in attr_to_prop.items():
1438+ for fake_name, real_name in list(attr_to_prop.items()):
1439 prop = getattr(obj, real_name)
1440 if fake_name != real_name:
1441 property_registry.add_property(obj, prop, fake_name)
1442@@ -259,7 +264,7 @@
1443 return getattr(self._cls, attr)
1444
1445
1446-class SQLObjectBase(Storm):
1447+class SQLObjectBase(with_metaclass(SQLObjectMeta, Storm)):
1448 """The root class of all SQLObject-emulating classes in your application.
1449
1450 The general strategy for using Storm's SQLObject emulation layer
1451@@ -269,7 +274,6 @@
1452 even be implemented as returning a global L{Store} instance. Then
1453 all database classes should subclass that class.
1454 """
1455- __metaclass__ = SQLObjectMeta
1456
1457 q = DotQ()
1458 _SO_creating = False
1459@@ -296,7 +300,7 @@
1460 self._init(None)
1461
1462 def set(self, **kwargs):
1463- for attr, value in kwargs.iteritems():
1464+ for attr, value in kwargs.items():
1465 setattr(self, attr, value)
1466
1467 def destroySelf(self):
1468@@ -329,7 +333,7 @@
1469 if not isinstance(orderBy, (tuple, list)):
1470 orderBy = (orderBy,)
1471 for item in orderBy:
1472- if isinstance(item, basestring):
1473+ if isinstance(item, six.string_types):
1474 desc = item.startswith("-")
1475 if desc:
1476 item = item[1:]
1477@@ -404,7 +408,7 @@
1478
1479 def _copy(self, **kwargs):
1480 copy = self.__class__(self._cls, **kwargs)
1481- for name, value in self.__dict__.iteritems():
1482+ for name, value in self.__dict__.items():
1483 if name[1:] not in kwargs and name != "_finished_result_set":
1484 setattr(copy, name, value)
1485 return copy
1486@@ -416,7 +420,7 @@
1487 if self._clause:
1488 args.append(self._clause)
1489
1490- for key, value in self._by.items():
1491+ for key, value in list(self._by.items()):
1492 args.append(getattr(self._cls, key) == value)
1493
1494 tables = []
1495@@ -548,7 +552,7 @@
1496 result_set = self._without_prejoins()._result_set
1497 return item in result_set
1498
1499- def __nonzero__(self):
1500+ def __bool__(self):
1501 """Return C{True} if this result set contains any results.
1502
1503 @note: This method is provided for compatibility with SQL Object. For
1504@@ -603,7 +607,7 @@
1505 return self._copy(prejoinClauseTables=prejoinClauseTables)
1506
1507 def sum(self, attribute):
1508- if isinstance(attribute, basestring):
1509+ if isinstance(attribute, six.string_types):
1510 attribute = SQL(attribute)
1511 result_set = self._without_prejoins()._result_set
1512 return result_set.sum(attribute)
1513@@ -679,9 +683,9 @@
1514 __slots__ = ()
1515
1516 def parse_set(self, value, from_db):
1517- if not isinstance(value, basestring):
1518+ if not isinstance(value, six.string_types):
1519 raise TypeError("Expected basestring, found %s" % repr(type(value)))
1520- return unicode(value)
1521+ return six.text_type(value)
1522
1523 class AutoUnicode(SimpleProperty):
1524 variable_class = AutoUnicodeVariable
1525
1526=== modified file 'storm/store.py'
1527--- storm/store.py 2015-04-20 08:46:56 +0000
1528+++ storm/store.py 2017-06-09 20:57:05 +0000
1529@@ -24,6 +24,8 @@
1530 This module contains the highest-level ORM interface in Storm.
1531 """
1532
1533+from builtins import zip, object
1534+import six
1535 from copy import copy
1536 from weakref import WeakValueDictionary
1537 from operator import itemgetter
1538@@ -470,7 +472,7 @@
1539 self._dirty = flushing
1540
1541 predecessors = {}
1542- for (before_info, after_info), n in self._order.iteritems():
1543+ for (before_info, after_info), n in self._order.items():
1544 if n > 0:
1545 before_set = predecessors.get(after_info)
1546 if before_set is None:
1547@@ -849,7 +851,7 @@
1548 del obj_info["primary_vars"]
1549
1550 def _iter_alive(self):
1551- return self._alive.values()
1552+ return list(self._alive.values())
1553
1554 def _enable_change_notification(self, obj_info):
1555 obj_info.event.emit("start-tracking-changes", self._event)
1556@@ -1003,7 +1005,7 @@
1557 L{ResultSet} will be returned appropriately modified with
1558 C{OFFSET} and C{LIMIT} clauses.
1559 """
1560- if isinstance(index, (int, long)):
1561+ if isinstance(index, six.integer_types):
1562 if index == 0:
1563 result_set = self
1564 else:
1565@@ -1377,7 +1379,7 @@
1566 "expression: %r" % repr(expr.expr2))
1567 changes[expr.expr1] = expr.expr2
1568
1569- for key, value in kwargs.items():
1570+ for key, value in list(kwargs.items()):
1571 column = getattr(cls, key)
1572 if value is None:
1573 changes[column] = None
1574@@ -1402,7 +1404,7 @@
1575 for column in changes:
1576 obj_info.variables[column].set(AutoReload)
1577 else:
1578- changes = changes.items()
1579+ changes = list(changes.items())
1580 for obj in cached:
1581 for column, value in changes:
1582 variables = get_obj_info(obj).variables
1583@@ -1791,7 +1793,7 @@
1584 if cls is None:
1585 raise FeatureError("Can't determine class that keyword "
1586 "arguments are associated with")
1587- for key, value in kwargs.items():
1588+ for key, value in list(kwargs.items()):
1589 equals.append(getattr(cls, key) == value)
1590 if equals:
1591 return And(*equals)
1592
1593=== modified file 'storm/tracer.py'
1594--- storm/tracer.py 2012-06-28 13:08:48 +0000
1595+++ storm/tracer.py 2017-06-09 20:57:05 +0000
1596@@ -1,3 +1,5 @@
1597+from builtins import range, object
1598+import six
1599 from datetime import datetime
1600 import re
1601 import sys
1602@@ -169,7 +171,7 @@
1603 # string parameters which represent encoded binary data.
1604 render_params = []
1605 for param in query_params:
1606- if isinstance(param, unicode):
1607+ if isinstance(param, six.text_type):
1608 render_params.append(repr(param.encode('utf8')))
1609 else:
1610 render_params.append(repr(param))
1611
1612=== modified file 'storm/twisted/testing.py'
1613--- storm/twisted/testing.py 2011-11-30 14:16:50 +0000
1614+++ storm/twisted/testing.py 2017-06-09 20:57:05 +0000
1615@@ -1,3 +1,4 @@
1616+from builtins import object
1617 import transaction
1618
1619 from twisted.python.failure import Failure
1620
1621=== modified file 'storm/twisted/transact.py'
1622--- storm/twisted/transact.py 2011-12-07 10:39:57 +0000
1623+++ storm/twisted/transact.py 2017-06-09 20:57:05 +0000
1624@@ -1,3 +1,4 @@
1625+from builtins import object
1626 import time
1627 import random
1628 import transaction
1629@@ -75,7 +76,7 @@
1630 try:
1631 result = function(*args, **kwargs)
1632 self._transaction.commit()
1633- except RETRIABLE_ERRORS, error:
1634+ except RETRIABLE_ERRORS as error:
1635 if isinstance(error, DisconnectionError):
1636 # If we got a disconnection, calling rollback may not be
1637 # enough because psycopg2 doesn't necessarily use the
1638
1639=== modified file 'storm/tz.py'
1640--- storm/tz.py 2007-08-07 18:36:04 +0000
1641+++ storm/tz.py 2017-06-09 20:57:05 +0000
1642@@ -7,6 +7,10 @@
1643 __author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
1644 __license__ = "PSF License"
1645
1646+from builtins import range
1647+from builtins import object
1648+import six
1649+
1650 import datetime
1651 import struct
1652 import time
1653@@ -75,7 +79,7 @@
1654
1655 def __repr__(self):
1656 return "%s(%s, %s)" % (self.__class__.__name__,
1657- `self._name`,
1658+ repr(self._name),
1659 self._offset.days*86400+self._offset.seconds)
1660
1661 __reduce__ = object.__reduce__
1662@@ -161,7 +165,7 @@
1663 for attr in self.__slots__:
1664 value = getattr(self, attr)
1665 if value is not None:
1666- l.append("%s=%s" % (attr, `value`))
1667+ l.append("%s=%s" % (attr, repr(value)))
1668 return "%s(%s)" % (self.__class__.__name__, ", ".join(l))
1669
1670 def __eq__(self, other):
1671@@ -194,13 +198,13 @@
1672 # ftp://elsie.nci.nih.gov/pub/tz*.tar.gz
1673
1674 def __init__(self, fileobj):
1675- if isinstance(fileobj, basestring):
1676+ if isinstance(fileobj, six.string_types):
1677 self._filename = fileobj
1678 fileobj = open(fileobj)
1679 elif hasattr(fileobj, "name"):
1680 self._filename = fileobj.name
1681 else:
1682- self._filename = `fileobj`
1683+ self._filename = repr(fileobj)
1684
1685 # From tzfile(5):
1686 #
1687@@ -213,7 +217,7 @@
1688 # of the value is written first).
1689
1690 if fileobj.read(4) != "TZif":
1691- raise ValueError, "magic not found"
1692+ raise ValueError("magic not found")
1693
1694 fileobj.read(16)
1695
1696@@ -465,11 +469,11 @@
1697
1698
1699 def __repr__(self):
1700- return "%s(%s)" % (self.__class__.__name__, `self._filename`)
1701+ return "%s(%s)" % (self.__class__.__name__, repr(self._filename))
1702
1703 def __reduce__(self):
1704 if not os.path.isfile(self._filename):
1705- raise ValueError, "Unpickable %s class" % self.__class__.__name__
1706+ raise ValueError("Unpickable %s class" % self.__class__.__name__)
1707 return (self.__class__, (self._filename,))
1708
1709 class tzrange(datetime.tzinfo):
1710@@ -561,7 +565,7 @@
1711
1712 res = parser._parsetz(s)
1713 if res is None:
1714- raise ValueError, "unknown string format"
1715+ raise ValueError("unknown string format")
1716
1717 # We must initialize it first, since _delta() needs
1718 # _std_offset and _dst_offset set. Use False in start/end
1719@@ -615,9 +619,9 @@
1720 return relativedelta.relativedelta(**kwargs)
1721
1722 def __repr__(self):
1723- return "%s(%s)" % (self.__class__.__name__, `self._s`)
1724+ return "%s(%s)" % (self.__class__.__name__, repr(self._s))
1725
1726-class _tzicalvtzcomp:
1727+class _tzicalvtzcomp(object):
1728 def __init__(self, tzoffsetfrom, tzoffsetto, isdst,
1729 tzname=None, rrule=None):
1730 self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom)
1731@@ -685,45 +689,45 @@
1732 return self._find_comp(dt).tzname
1733
1734 def __repr__(self):
1735- return "<tzicalvtz %s>" % `self._tzid`
1736+ return "<tzicalvtz %s>" % repr(self._tzid)
1737
1738 __reduce__ = object.__reduce__
1739
1740-class tzical:
1741+class tzical(object):
1742 def __init__(self, fileobj):
1743 global rrule
1744 if not rrule:
1745 from dateutil import rrule
1746
1747- if isinstance(fileobj, basestring):
1748+ if isinstance(fileobj, six.string_types):
1749 self._s = fileobj
1750 fileobj = open(fileobj)
1751 elif hasattr(fileobj, "name"):
1752 self._s = fileobj.name
1753 else:
1754- self._s = `fileobj`
1755+ self._s = repr(fileobj)
1756
1757 self._vtz = {}
1758
1759 self._parse_rfc(fileobj.read())
1760
1761 def keys(self):
1762- return self._vtz.keys()
1763+ return list(self._vtz.keys())
1764
1765 def get(self, tzid=None):
1766 if tzid is None:
1767- keys = self._vtz.keys()
1768+ keys = list(self._vtz.keys())
1769 if len(keys) == 0:
1770- raise "no timezones defined"
1771+ raise Exception("no timezones defined")
1772 elif len(keys) > 1:
1773- raise "more than one timezone available"
1774+ raise Exception("more than one timezone available")
1775 tzid = keys[0]
1776 return self._vtz.get(tzid)
1777
1778 def _parse_offset(self, s):
1779 s = s.strip()
1780 if not s:
1781- raise ValueError, "empty offset"
1782+ raise ValueError("empty offset")
1783 if s[0] in ('+', '-'):
1784 signal = (-1,+1)[s[0]=='+']
1785 s = s[1:]
1786@@ -734,12 +738,12 @@
1787 elif len(s) == 6:
1788 return (int(s[:2])*3600+int(s[2:4])*60+int(s[4:]))*signal
1789 else:
1790- raise ValueError, "invalid offset: "+s
1791+ raise ValueError("invalid offset: "+s)
1792
1793 def _parse_rfc(self, s):
1794 lines = s.splitlines()
1795 if not lines:
1796- raise ValueError, "empty string"
1797+ raise ValueError("empty string")
1798
1799 # Unfold
1800 i = 0
1801@@ -763,7 +767,7 @@
1802 name, value = line.split(':', 1)
1803 parms = name.split(';')
1804 if not parms:
1805- raise ValueError, "empty property name"
1806+ raise ValueError("empty property name")
1807 name = parms[0].upper()
1808 parms = parms[1:]
1809 if invtz:
1810@@ -772,7 +776,7 @@
1811 # Process component
1812 pass
1813 else:
1814- raise ValueError, "unknown component: "+value
1815+ raise ValueError("unknown component: "+value)
1816 comptype = value
1817 founddtstart = False
1818 tzoffsetfrom = None
1819@@ -782,27 +786,21 @@
1820 elif name == "END":
1821 if value == "VTIMEZONE":
1822 if comptype:
1823- raise ValueError, \
1824- "component not closed: "+comptype
1825+ raise ValueError("component not closed: "+comptype)
1826 if not tzid:
1827- raise ValueError, \
1828- "mandatory TZID not found"
1829+ raise ValueError("mandatory TZID not found")
1830 if not comps:
1831- raise ValueError, \
1832- "at least one component is needed"
1833+ raise ValueError("at least one component is needed")
1834 # Process vtimezone
1835 self._vtz[tzid] = _tzicalvtz(tzid, comps)
1836 invtz = False
1837 elif value == comptype:
1838 if not founddtstart:
1839- raise ValueError, \
1840- "mandatory DTSTART not found"
1841+ raise ValueError("mandatory DTSTART not found")
1842 if tzoffsetfrom is None:
1843- raise ValueError, \
1844- "mandatory TZOFFSETFROM not found"
1845+ raise ValueError("mandatory TZOFFSETFROM not found")
1846 if tzoffsetto is None:
1847- raise ValueError, \
1848- "mandatory TZOFFSETFROM not found"
1849+ raise ValueError("mandatory TZOFFSETFROM not found")
1850 # Process component
1851 rr = None
1852 if rrulelines:
1853@@ -816,8 +814,7 @@
1854 comps.append(comp)
1855 comptype = None
1856 else:
1857- raise ValueError, \
1858- "invalid component end: "+value
1859+ raise ValueError("invalid component end: "+value)
1860 elif comptype:
1861 if name == "DTSTART":
1862 rrulelines.append(line)
1863@@ -826,40 +823,36 @@
1864 rrulelines.append(line)
1865 elif name == "TZOFFSETFROM":
1866 if parms:
1867- raise ValueError, \
1868- "unsupported %s parm: %s "%(name, parms[0])
1869+ raise ValueError("unsupported %s parm: %s "%(name, parms[0]))
1870 tzoffsetfrom = self._parse_offset(value)
1871 elif name == "TZOFFSETTO":
1872 if parms:
1873- raise ValueError, \
1874- "unsupported TZOFFSETTO parm: "+parms[0]
1875+ raise ValueError("unsupported TZOFFSETTO parm: "+parms[0])
1876 tzoffsetto = self._parse_offset(value)
1877 elif name == "TZNAME":
1878 if parms:
1879- raise ValueError, \
1880- "unsupported TZNAME parm: "+parms[0]
1881+ raise ValueError("unsupported TZNAME parm: "+parms[0])
1882 tzname = value
1883 elif name == "COMMENT":
1884 pass
1885 else:
1886- raise ValueError, "unsupported property: "+name
1887+ raise ValueError("unsupported property: "+name)
1888 else:
1889 if name == "TZID":
1890 if parms:
1891- raise ValueError, \
1892- "unsupported TZID parm: "+parms[0]
1893+ raise ValueError("unsupported TZID parm: "+parms[0])
1894 tzid = value
1895 elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"):
1896 pass
1897 else:
1898- raise ValueError, "unsupported property: "+name
1899+ raise ValueError("unsupported property: "+name)
1900 elif name == "BEGIN" and value == "VTIMEZONE":
1901 tzid = None
1902 comps = []
1903 invtz = True
1904
1905 def __repr__(self):
1906- return "%s(%s)" % (self.__class__.__name__, `self._s`)
1907+ return "%s(%s)" % (self.__class__.__name__, repr(self._s))
1908
1909 if sys.platform != "win32":
1910 TZFILES = ["/etc/localtime", "localtime"]
1911
1912=== modified file 'storm/uri.py'
1913--- storm/uri.py 2008-01-30 13:03:27 +0000
1914+++ storm/uri.py 2017-06-09 20:57:05 +0000
1915@@ -18,7 +18,12 @@
1916 # You should have received a copy of the GNU Lesser General Public License
1917 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1918 #
1919-from urllib import quote
1920+from future import standard_library
1921+standard_library.install_aliases()
1922+from builtins import chr
1923+from builtins import str
1924+from builtins import object
1925+from urllib.parse import quote
1926
1927 from storm.exceptions import URIError
1928
1929@@ -102,7 +107,7 @@
1930 append(escape(self.database, "/"))
1931 if self.options:
1932 options = ["%s=%s" % (escape(key), escape(value))
1933- for key, value in sorted(self.options.iteritems())]
1934+ for key, value in sorted(self.options.items())]
1935 append("?")
1936 append("&".join(options))
1937 return "".join(tokens)
1938
1939=== modified file 'storm/variables.py'
1940--- storm/variables.py 2011-09-13 10:13:46 +0000
1941+++ storm/variables.py 2017-06-09 20:57:05 +0000
1942@@ -1,3 +1,4 @@
1943+from __future__ import unicode_literals
1944 #
1945 # Copyright (c) 2006, 2007 Canonical
1946 #
1947@@ -18,9 +19,13 @@
1948 # You should have received a copy of the GNU Lesser General Public License
1949 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1950 #
1951+from future import standard_library
1952+standard_library.install_aliases()
1953+from builtins import object
1954+import six
1955 from datetime import datetime, date, time, timedelta
1956 from decimal import Decimal
1957-import cPickle as pickle
1958+import pickle as pickle
1959 import re
1960 try:
1961 import uuid
1962@@ -31,6 +36,7 @@
1963 from storm.exceptions import NoneError
1964 from storm import Undef, has_cextensions
1965
1966+buffer = buffer if six.PY2 else memoryview
1967
1968 __all__ = [
1969 "VariableFactory",
1970@@ -329,7 +335,7 @@
1971 __slots__ = ()
1972
1973 def parse_set(self, value, from_db):
1974- if not isinstance(value, (int, long, float, Decimal)):
1975+ if not isinstance(value, six.integer_types + (float, Decimal)):
1976 raise TypeError("Expected bool, found %r: %r"
1977 % (type(value), value))
1978 return bool(value)
1979@@ -339,7 +345,7 @@
1980 __slots__ = ()
1981
1982 def parse_set(self, value, from_db):
1983- if not isinstance(value, (int, long, float, Decimal)):
1984+ if not isinstance(value, six.integer_types + (float, Decimal)):
1985 raise TypeError("Expected int, found %r: %r"
1986 % (type(value), value))
1987 return int(value)
1988@@ -349,7 +355,7 @@
1989 __slots__ = ()
1990
1991 def parse_set(self, value, from_db):
1992- if not isinstance(value, (int, long, float, Decimal)):
1993+ if not isinstance(value, six.integer_types + (float, Decimal)):
1994 raise TypeError("Expected float, found %r: %r"
1995 % (type(value), value))
1996 return float(value)
1997@@ -360,8 +366,8 @@
1998
1999 @staticmethod
2000 def parse_set(value, from_db):
2001- if (from_db and isinstance(value, basestring) or
2002- isinstance(value, (int, long))):
2003+ if (from_db and isinstance(value, six.string_types) or
2004+ isinstance(value, six.integer_types)):
2005 value = Decimal(value)
2006 elif not isinstance(value, Decimal):
2007 raise TypeError("Expected Decimal, found %r: %r"
2008@@ -371,7 +377,7 @@
2009 @staticmethod
2010 def parse_get(value, to_db):
2011 if to_db:
2012- return unicode(value)
2013+ return six.text_type(value)
2014 return value
2015
2016
2017@@ -380,9 +386,9 @@
2018
2019 def parse_set(self, value, from_db):
2020 if isinstance(value, buffer):
2021- value = str(value)
2022- elif not isinstance(value, str):
2023- raise TypeError("Expected str, found %r: %r"
2024+ value = six.binary_type(value)
2025+ elif not isinstance(value, six.binary_type):
2026+ raise TypeError("Expected bytes, found %r: %r"
2027 % (type(value), value))
2028 return value
2029
2030@@ -391,7 +397,7 @@
2031 __slots__ = ()
2032
2033 def parse_set(self, value, from_db):
2034- if not isinstance(value, unicode):
2035+ if not isinstance(value, six.text_type):
2036 raise TypeError("Expected unicode, found %r: %r"
2037 % (type(value), value))
2038 return value
2039@@ -408,7 +414,7 @@
2040 if from_db:
2041 if isinstance(value, datetime):
2042 pass
2043- elif isinstance(value, (str, unicode)):
2044+ elif isinstance(value, six.string_types):
2045 if " " not in value:
2046 raise ValueError("Unknown date/time format: %r" % value)
2047 date_str, time_str = value.split(" ")
2048@@ -422,7 +428,7 @@
2049 else:
2050 value = value.astimezone(self._tzinfo)
2051 else:
2052- if type(value) in (int, long, float):
2053+ if type(value) in six.integer_types + (float, ):
2054 value = datetime.utcfromtimestamp(value)
2055 elif not isinstance(value, datetime):
2056 raise TypeError("Expected datetime, found %s" % repr(value))
2057@@ -442,7 +448,7 @@
2058 return value.date()
2059 if isinstance(value, date):
2060 return value
2061- if not isinstance(value, (str, unicode)):
2062+ if not isinstance(value, six.string_types):
2063 raise TypeError("Expected date, found %s" % repr(value))
2064 if " " in value:
2065 value, time_str = value.split(" ")
2066@@ -465,7 +471,7 @@
2067 return None
2068 if isinstance(value, time):
2069 return value
2070- if not isinstance(value, (str, unicode)):
2071+ if not isinstance(value, six.string_types):
2072 raise TypeError("Expected time, found %s" % repr(value))
2073 if " " in value:
2074 date_str, value = value.split(" ")
2075@@ -488,7 +494,7 @@
2076 return None
2077 if isinstance(value, timedelta):
2078 return value
2079- if not isinstance(value, (str, unicode)):
2080+ if not isinstance(value, six.string_types):
2081 raise TypeError("Expected timedelta, found %s" % repr(value))
2082 return _parse_interval(value)
2083 else:
2084@@ -502,7 +508,7 @@
2085
2086 def parse_set(self, value, from_db):
2087 assert uuid is not None, "The uuid module was not found."
2088- if from_db and isinstance(value, basestring):
2089+ if from_db and isinstance(value, six.string_types):
2090 value = uuid.UUID(value)
2091 elif not isinstance(value, uuid.UUID):
2092 raise TypeError("Expected UUID, found %r: %r"
2093@@ -511,7 +517,7 @@
2094
2095 def parse_get(self, value, to_db):
2096 if to_db:
2097- return unicode(value)
2098+ return six.text_type(value)
2099 return value
2100
2101
2102@@ -595,7 +601,7 @@
2103 def parse_set(self, value, from_db):
2104 if from_db:
2105 if isinstance(value, buffer):
2106- value = str(value)
2107+ value = six.binary_type(value)
2108 return self._loads(value)
2109 else:
2110 return value
2111@@ -633,7 +639,7 @@
2112 super(JSONVariable, self).__init__(*args, **kwargs)
2113
2114 def _loads(self, value):
2115- if not isinstance(value, unicode):
2116+ if not isinstance(value, six.text_type):
2117 raise TypeError(
2118 "Cannot safely assume encoding of byte string %r." % value)
2119 return json.loads(value)
2120@@ -643,7 +649,7 @@
2121 # and so we treat it as such here. In other words, this method returns
2122 # unicode and never str.
2123 dump = json.dumps(value, ensure_ascii=False)
2124- if not isinstance(dump, unicode):
2125+ if not isinstance(dump, six.text_type):
2126 # json.dumps() does not always return unicode. See
2127 # http://code.google.com/p/simplejson/issues/detail?id=40 for one
2128 # of many discussions of str/unicode handling in simplejson.
2129
2130=== modified file 'storm/xid.py'
2131--- storm/xid.py 2012-03-01 13:28:26 +0000
2132+++ storm/xid.py 2017-06-09 20:57:05 +0000
2133@@ -20,6 +20,7 @@
2134 #
2135
2136
2137+from builtins import object
2138 class Xid(object):
2139 """
2140 Represent a transaction identifier compliant with the XA specification.
2141
2142=== modified file 'storm/zope/schema.py'
2143--- storm/zope/schema.py 2010-08-17 14:00:59 +0000
2144+++ storm/zope/schema.py 2017-06-09 20:57:05 +0000
2145@@ -19,6 +19,7 @@
2146 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2147 #
2148 """ZStorm-aware schema manager."""
2149+from builtins import object
2150 import transaction
2151
2152 from storm.schema import Schema
2153
2154=== modified file 'storm/zope/testing.py'
2155--- storm/zope/testing.py 2014-12-18 11:37:22 +0000
2156+++ storm/zope/testing.py 2017-06-09 20:57:05 +0000
2157@@ -98,7 +98,7 @@
2158 # compatibility. This should be eventually dropped.
2159 if isinstance(databases, dict):
2160 databases = [{"name": name, "uri": uri, "schema": schema}
2161- for name, (uri, schema) in databases.iteritems()]
2162+ for name, (uri, schema) in databases.items()]
2163
2164 # Provide the global IZStorm utility before applying patches, so
2165 # patch code can get the ztorm object if needed (e.g. looking up
2166
2167=== modified file 'storm/zope/zstorm.py'
2168--- storm/zope/zstorm.py 2012-03-28 10:57:43 +0000
2169+++ storm/zope/zstorm.py 2017-06-09 20:57:05 +0000
2170@@ -24,6 +24,8 @@
2171 # You should have received a copy of the GNU Lesser General Public License
2172 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2173 #
2174+from builtins import str
2175+from builtins import object
2176 import threading
2177 import weakref
2178
2179@@ -209,7 +211,7 @@
2180 # avoid the problem where a store is deallocated during
2181 # iteration causing RuntimeError: dictionary changed size
2182 # during iteration.
2183- for store, name in self._name_index.items():
2184+ for store, name in list(self._name_index.items()):
2185 yield name, store
2186
2187 def get_name(self, store):
2188
2189=== modified file 'tests/cache.py'
2190--- tests/cache.py 2011-07-14 11:15:30 +0000
2191+++ tests/cache.py 2017-06-09 20:57:05 +0000
2192@@ -1,3 +1,6 @@
2193+from builtins import str
2194+from builtins import range
2195+from builtins import object
2196 from unittest import defaultTestLoader
2197
2198 from storm.properties import Int
2199@@ -134,7 +137,7 @@
2200 """
2201 size = 10
2202 cache = self.Cache(size)
2203- for value in xrange(size):
2204+ for value in range(size):
2205 cache.add(StubObjectInfo(value))
2206 self.assertEqual(len(cache.get_cached()), size)
2207
2208@@ -255,7 +258,7 @@
2209 """
2210 size = 10
2211 cache = GenerationalCache(size)
2212- for value in xrange(5 * size):
2213+ for value in range(5 * size):
2214 cache.add(StubObjectInfo(value))
2215 self.assertEquals(len(cache.get_cached()), size * 2)
2216
2217@@ -298,7 +301,7 @@
2218 size = 10
2219 cache = GenerationalCache(size * 100)
2220 cache.set_size(size)
2221- for value in xrange(size * 10):
2222+ for value in range(size * 10):
2223 cache.add(StubObjectInfo(value))
2224 self.assertEquals(len(cache.get_cached()), size * 2)
2225
2226
2227=== modified file 'tests/database.py'
2228--- tests/database.py 2013-02-15 17:00:18 +0000
2229+++ tests/database.py 2017-06-09 20:57:05 +0000
2230@@ -18,6 +18,9 @@
2231 # You should have received a copy of the GNU Lesser General Public License
2232 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2233 #
2234+from builtins import str
2235+from builtins import range
2236+from builtins import object
2237 import sys
2238 import new
2239 import gc
2240
2241=== modified file 'tests/databases/base.py'
2242--- tests/databases/base.py 2015-06-15 12:06:44 +0000
2243+++ tests/databases/base.py 2017-06-09 20:57:05 +0000
2244@@ -20,8 +20,12 @@
2245 # You should have received a copy of the GNU Lesser General Public License
2246 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2247 #
2248+from future import standard_library
2249+standard_library.install_aliases()
2250+from builtins import next, str, object
2251+import six
2252 from datetime import datetime, date, time, timedelta
2253-import cPickle as pickle
2254+import pickle as pickle
2255 import shutil
2256 import sys
2257 import os
2258@@ -149,7 +153,7 @@
2259 self.assertTrue(isinstance(result, Result))
2260 row = result.get_one()
2261 self.assertEquals(row, ("Title 10",))
2262- self.assertTrue(isinstance(row[0], unicode))
2263+ self.assertTrue(isinstance(row[0], six.text_type))
2264
2265 def test_execute_params(self):
2266 result = self.connection.execute("SELECT one FROM number "
2267@@ -192,12 +196,12 @@
2268 "ORDER BY id DESC")
2269 iter1 = iter(result1)
2270 iter2 = iter(result2)
2271- self.assertEquals(iter1.next(), (10, "Title 10"))
2272- self.assertEquals(iter2.next(), (20, "Title 20"))
2273- self.assertEquals(iter1.next(), (20, "Title 20"))
2274- self.assertEquals(iter2.next(), (10, "Title 10"))
2275- self.assertRaises(StopIteration, iter1.next)
2276- self.assertRaises(StopIteration, iter2.next)
2277+ self.assertEquals(next(iter1), (10, "Title 10"))
2278+ self.assertEquals(next(iter2), (20, "Title 20"))
2279+ self.assertEquals(next(iter1), (20, "Title 20"))
2280+ self.assertEquals(next(iter2), (10, "Title 10"))
2281+ self.assertRaises(StopIteration, next, iter1)
2282+ self.assertRaises(StopIteration, next, iter2)
2283
2284 def test_get_insert_identity(self):
2285 result = self.connection.execute("INSERT INTO test (title) "
2286@@ -346,7 +350,7 @@
2287 connection2.execute("UPDATE test SET title='Title 100' "
2288 "WHERE id=10")
2289 connection2.commit()
2290- except OperationalError, e:
2291+ except OperationalError as e:
2292 self.assertEquals(str(e), "database is locked") # SQLite blocks
2293 result = connection1.execute("SELECT title FROM test WHERE id=10")
2294 self.assertEquals(result.get_one(), ("Title 10",))
2295@@ -385,7 +389,7 @@
2296 def test_wb_result_iter_goes_through_from_database(self):
2297 result = self.connection.execute("SELECT one, two FROM number")
2298 result.from_database = self.from_database
2299- self.assertEquals(iter(result).next(), (2, 3))
2300+ self.assertEquals(next(iter(result)), (2, 3))
2301
2302 def test_rowcount_insert(self):
2303 # All supported backends support rowcount, so far.
2304@@ -772,7 +776,7 @@
2305 cursor = self.connection._raw_connection.cursor()
2306 cursor.execute("SELECT 1")
2307 cursor.fetchone()
2308- except Error, exc:
2309+ except Error as exc:
2310 self.assertTrue(self.connection.is_disconnection_error(exc))
2311 else:
2312 self.fail("Disconnection was not caught.")
2313@@ -781,7 +785,7 @@
2314 # error when called.
2315 try:
2316 self.connection._raw_connection.rollback()
2317- except Error, exc:
2318+ except Error as exc:
2319 self.assertTrue(self.connection.is_disconnection_error(exc))
2320 else:
2321 self.fail("Disconnection was not raised.")
2322@@ -805,7 +809,7 @@
2323 cursor = self.connection._raw_connection.cursor()
2324 cursor.execute("SELECT 1")
2325 cursor.fetchone()
2326- except DatabaseError, exc:
2327+ except DatabaseError as exc:
2328 self.assertTrue(self.connection.is_disconnection_error(exc))
2329 else:
2330 self.fail("Disconnection was not caught.")
2331
2332=== modified file 'tests/databases/postgres.py'
2333--- tests/databases/postgres.py 2016-04-29 08:38:51 +0000
2334+++ tests/databases/postgres.py 2017-06-09 20:57:05 +0000
2335@@ -18,6 +18,8 @@
2336 # You should have received a copy of the GNU Lesser General Public License
2337 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2338 #
2339+from builtins import object
2340+import six
2341 from datetime import date, time, timedelta
2342 import os
2343 import json
2344@@ -154,7 +156,7 @@
2345 result = connection.execute("SELECT title FROM test WHERE id=1")
2346 title = result.get_one()[0]
2347
2348- self.assertTrue(isinstance(title, unicode))
2349+ self.assertTrue(isinstance(title, six.text_type))
2350 self.assertEquals(title, uni_str)
2351
2352 def test_unicode_array(self):
2353@@ -735,13 +737,13 @@
2354 InterfaceErrors are a form of a disconnection error, so rollback()
2355 must swallow them and reconnect.
2356 """
2357- class FakeConnection:
2358+ class FakeConnection(object):
2359 def rollback(self):
2360 raise InterfaceError('connection already closed')
2361 self.connection._raw_connection = FakeConnection()
2362 try:
2363 self.connection.rollback()
2364- except Exception, exc:
2365+ except Exception as exc:
2366 self.fail('Exception should have been swallowed: %s' % repr(exc))
2367
2368
2369@@ -875,7 +877,7 @@
2370 self.remaining_time = 0.001
2371 try:
2372 self.connection.execute(statement)
2373- except TimeoutError, e:
2374+ except TimeoutError as e:
2375 self.assertEqual("SQL server cancelled statement", e.message)
2376 self.assertEqual(statement, e.statement)
2377 self.assertEqual((), e.params)
2378
2379=== modified file 'tests/databases/proxy.py'
2380--- tests/databases/proxy.py 2007-10-24 06:27:06 +0000
2381+++ tests/databases/proxy.py 2017-06-09 20:57:05 +0000
2382@@ -20,22 +20,24 @@
2383 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2384 #
2385
2386+from future import standard_library
2387+standard_library.install_aliases()
2388 import os
2389 import select
2390 import socket
2391-import SocketServer
2392+import socketserver
2393 import threading
2394
2395
2396 TIMEOUT = 0.1
2397
2398
2399-class ProxyRequestHandler(SocketServer.BaseRequestHandler):
2400+class ProxyRequestHandler(socketserver.BaseRequestHandler):
2401 """A request handler that proxies traffic to another TCP port."""
2402
2403 def __init__(self, request, client_address, server):
2404 self._generation = server._generation
2405- SocketServer.BaseRequestHandler.__init__(
2406+ socketserver.BaseRequestHandler.__init__(
2407 self, request, client_address, server)
2408
2409 def handle(self):
2410@@ -66,12 +68,12 @@
2411 self.request.shutdown(socket.SHUT_WR)
2412
2413
2414-class ProxyTCPServer(SocketServer.ThreadingTCPServer):
2415+class ProxyTCPServer(socketserver.ThreadingTCPServer):
2416
2417 allow_reuse_address = True
2418
2419 def __init__(self, proxy_dest):
2420- SocketServer.ThreadingTCPServer.__init__(
2421+ socketserver.ThreadingTCPServer.__init__(
2422 self, ("127.0.0.1", 0), ProxyRequestHandler)
2423 # Python 2.4 doesn't retrieve the socket details, so record
2424 # them here. We need to do this so we can recreate the socket
2425
2426=== modified file 'tests/databases/sqlite.py'
2427--- tests/databases/sqlite.py 2013-05-05 10:36:13 +0000
2428+++ tests/databases/sqlite.py 2017-06-09 20:57:05 +0000
2429@@ -18,6 +18,7 @@
2430 # You should have received a copy of the GNU Lesser General Public License
2431 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2432 #
2433+from builtins import str
2434 from datetime import timedelta
2435 import time
2436 import os
2437@@ -114,7 +115,7 @@
2438 started = time.time()
2439 try:
2440 connection2.execute("INSERT INTO test VALUES (2)")
2441- except OperationalError, exception:
2442+ except OperationalError as exception:
2443 self.assertEquals(str(exception), "database is locked")
2444 self.assertTrue(time.time()-started >= 0.3)
2445 else:
2446@@ -141,7 +142,7 @@
2447 started = time.time()
2448 try:
2449 connection1.commit()
2450- except OperationalError, exception:
2451+ except OperationalError as exception:
2452 self.assertEquals(str(exception), "database is locked")
2453 # In 0.10, the next assertion failed because the timeout wasn't
2454 # enforced for the "COMMIT" statement.
2455
2456=== modified file 'tests/event.py'
2457--- tests/event.py 2008-06-18 15:27:03 +0000
2458+++ tests/event.py 2017-06-09 20:57:05 +0000
2459@@ -18,6 +18,7 @@
2460 # You should have received a copy of the GNU Lesser General Public License
2461 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2462 #
2463+from builtins import object
2464 from storm.event import EventSystem
2465
2466 from tests.helper import TestHelper
2467
2468=== modified file 'tests/expr.py'
2469--- tests/expr.py 2012-03-26 14:27:29 +0000
2470+++ tests/expr.py 2017-06-09 20:57:05 +0000
2471@@ -1,3 +1,4 @@
2472+from __future__ import division
2473 #
2474 # Copyright (c) 2006, 2007 Canonical
2475 #
2476@@ -18,6 +19,8 @@
2477 # You should have received a copy of the GNU Lesser General Public License
2478 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2479 #
2480+from builtins import range
2481+from builtins import object
2482 from decimal import Decimal
2483
2484 from tests.helper import TestHelper
2485@@ -35,9 +38,9 @@
2486 # Create columnN, tableN, and elemN variables.
2487 for i in range(10):
2488 for name in ["column", "elem"]:
2489- exec "%s%d = SQLToken('%s%d')" % (name, i, name, i)
2490+ exec("%s%d = SQLToken('%s%d')" % (name, i, name, i))
2491 for name in ["table"]:
2492- exec "%s%d = '%s %d'" % (name, i, name, i)
2493+ exec("%s%d = '%s %d'" % (name, i, name, i))
2494
2495
2496 class TrackContext(FromExpr):
2497@@ -495,7 +498,7 @@
2498
2499 def test_precedence(self):
2500 for i in range(10):
2501- exec "e%d = SQLRaw('%d')" % (i, i)
2502+ exec("e%d = SQLRaw('%d')" % (i, i))
2503 expr = And(e1, Or(e2, e3),
2504 Add(e4, Mul(e5, Sub(e6, Div(e7, Div(e8, e9))))))
2505 statement = compile(expr)
2506@@ -586,7 +589,7 @@
2507
2508 def test_long(self):
2509 state = State()
2510- statement = compile(1L, state)
2511+ statement = compile(1, state)
2512 self.assertEquals(statement, "?")
2513 self.assertVariablesEqual(state.parameters, [IntVariable(1)])
2514
2515@@ -1360,7 +1363,7 @@
2516 self.assertEquals(statement, "elem1/elem2/elem3")
2517 self.assertEquals(state.parameters, [])
2518
2519- expr = Func1() / "value"
2520+ expr = Func1() // "value"
2521 state = State()
2522 statement = compile(expr, state)
2523 self.assertEquals(statement, "func1()/?")
2524@@ -2130,7 +2133,7 @@
2525
2526 def test_precedence(self):
2527 for i in range(10):
2528- exec "e%d = SQLRaw('%d')" % (i, i)
2529+ exec("e%d = SQLRaw('%d')" % (i, i))
2530 expr = And(e1, Or(e2, e3),
2531 Add(e4, Mul(e5, Sub(e6, Div(e7, Div(e8, e9))))))
2532 py_expr = compile_python(expr)
2533@@ -2172,7 +2175,7 @@
2534 self.assertEquals(py_expr, "1")
2535
2536 def test_long(self):
2537- py_expr = compile_python(1L)
2538+ py_expr = compile_python(1)
2539 self.assertEquals(py_expr, "1L")
2540
2541 def test_bool(self):
2542
2543=== modified file 'tests/helper.py'
2544--- tests/helper.py 2012-04-04 23:42:51 +0000
2545+++ tests/helper.py 2017-06-09 20:57:05 +0000
2546@@ -18,7 +18,11 @@
2547 # You should have received a copy of the GNU Lesser General Public License
2548 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2549 #
2550-from cStringIO import StringIO
2551+from future import standard_library
2552+standard_library.install_aliases()
2553+from builtins import zip
2554+from builtins import object
2555+from io import StringIO
2556 import tempfile
2557 import logging
2558 import shutil
2559
2560=== modified file 'tests/info.py'
2561--- tests/info.py 2011-12-07 11:57:07 +0000
2562+++ tests/info.py 2017-06-09 20:57:05 +0000
2563@@ -18,6 +18,8 @@
2564 # You should have received a copy of the GNU Lesser General Public License
2565 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2566 #
2567+from builtins import zip
2568+from builtins import object
2569 from weakref import ref
2570 import gc
2571
2572@@ -28,6 +30,7 @@
2573 from storm.info import *
2574
2575 from tests.helper import TestHelper
2576+from future.utils import with_metaclass
2577
2578
2579 class Wrapper(object):
2580@@ -569,8 +572,7 @@
2581 cls = type.__new__(meta_cls, name, bases, dict)
2582 cls.__storm_table__ = "HAH! GOTCH YA!"
2583 return cls
2584- class Class(object):
2585- __metaclass__ = MetaClass
2586+ class Class(with_metaclass(MetaClass, object)):
2587 __storm_table__ = "table"
2588 prop1 = Property("column1", primary=True)
2589 Alias = ClassAlias(Class, "USE_THIS")
2590
2591=== modified file 'tests/mocker.py'
2592--- tests/mocker.py 2008-05-18 10:22:29 +0000
2593+++ tests/mocker.py 2017-06-09 20:57:05 +0000
2594@@ -3,7 +3,13 @@
2595
2596 Graceful platform for test doubles in Python (mocks, stubs, fakes, and dummies).
2597 """
2598-import __builtin__
2599+from future import standard_library
2600+standard_library.install_aliases()
2601+from builtins import zip
2602+from builtins import str
2603+from builtins import range
2604+from builtins import object
2605+import builtins
2606 import tempfile
2607 import unittest
2608 import inspect
2609@@ -13,6 +19,8 @@
2610 import os
2611 import gc
2612
2613+import six
2614+
2615
2616 if sys.version_info < (2, 4):
2617 from sets import Set as set # pragma: nocover
2618@@ -274,7 +282,7 @@
2619 """
2620 first_methods = dict(inspect.getmembers(first, inspect.ismethod))
2621 second_methods = dict(inspect.getmembers(second, inspect.ismethod))
2622- for name, first_method in first_methods.items():
2623+ for name, first_method in list(first_methods.items()):
2624 first_argspec = inspect.getargspec(first_method)
2625 first_formatted = inspect.formatargspec(*first_argspec)
2626
2627@@ -487,7 +495,7 @@
2628 for event in self._events:
2629 try:
2630 event.verify()
2631- except AssertionError, e:
2632+ except AssertionError as e:
2633 error = str(e)
2634 if not error:
2635 raise RuntimeError("Empty error message from %r"
2636@@ -562,7 +570,7 @@
2637 explicitly requested via the L{passthrough()}
2638 method.
2639 """
2640- if isinstance(object, basestring):
2641+ if isinstance(object, six.string_types):
2642 if name is None:
2643 name = object
2644 import_stack = object.split(".")
2645@@ -583,7 +591,7 @@
2646 if spec is True:
2647 spec = object
2648 if type is True:
2649- type = __builtin__.type(object)
2650+ type = builtins.type(object)
2651 return Mock(self, spec=spec, type=type, object=object,
2652 name=name, count=count, passthrough=passthrough)
2653
2654@@ -1029,7 +1037,7 @@
2655 path.root_object = object
2656 try:
2657 return self.__mocker__.act(path)
2658- except MatchError, exception:
2659+ except MatchError as exception:
2660 root_mock = path.root_mock
2661 if (path.root_object is not None and
2662 root_mock.__mocker_passthrough__):
2663@@ -1037,7 +1045,7 @@
2664 # Reinstantiate to show raise statement on traceback, and
2665 # also to make the traceback shown shorter.
2666 raise MatchError(str(exception))
2667- except AssertionError, e:
2668+ except AssertionError as e:
2669 lines = str(e).splitlines()
2670 message = [ERROR_PREFIX + "Unmet expectation:", ""]
2671 message.append("=> " + lines.pop(0))
2672@@ -1083,16 +1091,16 @@
2673 # something that doesn't offer them.
2674 try:
2675 result = self.__mocker_act__("len")
2676- except MatchError, e:
2677+ except MatchError as e:
2678 raise AttributeError(str(e))
2679 if type(result) is Mock:
2680 return 0
2681 return result
2682
2683- def __nonzero__(self):
2684+ def __bool__(self):
2685 try:
2686 return self.__mocker_act__("nonzero")
2687- except MatchError, e:
2688+ except MatchError as e:
2689 return True
2690
2691 def __iter__(self):
2692@@ -1115,13 +1123,13 @@
2693 frame = sys._getframe(depth+1)
2694 except:
2695 return None
2696- for name, frame_obj in frame.f_locals.iteritems():
2697+ for name, frame_obj in frame.f_locals.items():
2698 if frame_obj is obj:
2699 return name
2700 self = frame.f_locals.get("self")
2701 if self is not None:
2702 try:
2703- items = list(self.__dict__.iteritems())
2704+ items = list(self.__dict__.items())
2705 except:
2706 pass
2707 else:
2708@@ -1270,7 +1278,7 @@
2709 result = "del %s.%s" % (result, action.args[0])
2710 elif action.kind == "call":
2711 args = [repr(x) for x in action.args]
2712- items = list(action.kwargs.iteritems())
2713+ items = list(action.kwargs.items())
2714 items.sort()
2715 for pair in items:
2716 args.append("%s=%r" % pair)
2717@@ -1390,7 +1398,7 @@
2718
2719 # Either we have the same number of kwargs, or unknown keywords are
2720 # accepted (KWARGS was used), so check just the ones in kwargs1.
2721- for key, arg1 in kwargs1.iteritems():
2722+ for key, arg1 in kwargs1.items():
2723 if key not in kwargs2:
2724 return False
2725 arg2 = kwargs2[key]
2726@@ -1519,7 +1527,7 @@
2727 for task in self._tasks:
2728 try:
2729 task_result = task.run(path)
2730- except AssertionError, e:
2731+ except AssertionError as e:
2732 error = str(e)
2733 if not error:
2734 raise RuntimeError("Empty error message from %r" % task)
2735@@ -1562,7 +1570,7 @@
2736 for task in self._tasks:
2737 try:
2738 task.verify()
2739- except AssertionError, e:
2740+ except AssertionError as e:
2741 error = str(e)
2742 if not error:
2743 raise RuntimeError("Empty error message from %r" % task)
2744@@ -1672,7 +1680,7 @@
2745 def __init__(self, min, max=False):
2746 self.min = min
2747 if max is None:
2748- self.max = sys.maxint
2749+ self.max = sys.maxsize
2750 elif max is False:
2751 self.max = min
2752 else:
2753@@ -1920,7 +1928,7 @@
2754 for referrer in gc.get_referrers(remove):
2755 if (type(referrer) is dict and
2756 referrer.get("__mocker_replace__", True)):
2757- for key, value in referrer.items():
2758+ for key, value in list(referrer.items()):
2759 if value is remove:
2760 referrer[key] = install
2761
2762@@ -1992,7 +2000,7 @@
2763 for kind in self._monitored:
2764 attr = self._get_kind_attr(kind)
2765 seen = set()
2766- for obj in self._monitored[kind].itervalues():
2767+ for obj in self._monitored[kind].values():
2768 cls = type(obj)
2769 if issubclass(cls, type):
2770 cls = obj
2771@@ -2006,7 +2014,7 @@
2772 self.execute)
2773
2774 def restore(self):
2775- for obj, attr, original in self._patched.itervalues():
2776+ for obj, attr, original in self._patched.values():
2777 if original is Undefined:
2778 delattr(obj, attr)
2779 else:
2780
2781=== modified file 'tests/properties.py'
2782--- tests/properties.py 2011-09-07 14:04:04 +0000
2783+++ tests/properties.py 2017-06-09 20:57:05 +0000
2784@@ -18,9 +18,11 @@
2785 # You should have received a copy of the GNU Lesser General Public License
2786 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2787 #
2788+from builtins import object
2789 from datetime import datetime, date, time, timedelta
2790 from decimal import Decimal as decimal
2791 import gc
2792+from future.utils import with_metaclass
2793 try:
2794 import uuid
2795 except ImportError:
2796@@ -991,8 +993,8 @@
2797 def setUp(self):
2798 TestHelper.setUp(self)
2799
2800- class Base(object):
2801- __metaclass__ = PropertyPublisherMeta
2802+ class Base(with_metaclass(PropertyPublisherMeta, object)):
2803+ pass
2804
2805 class Class(Base):
2806 __storm_table__ = "mytable"
2807
2808=== modified file 'tests/schema/patch.py'
2809--- tests/schema/patch.py 2014-12-18 10:57:52 +0000
2810+++ tests/schema/patch.py 2017-06-09 20:57:05 +0000
2811@@ -18,6 +18,8 @@
2812 # You should have received a copy of the GNU Lesser General Public License
2813 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2814 #
2815+from builtins import str
2816+from builtins import object
2817 import traceback
2818 import sys
2819 import os
2820@@ -345,7 +347,7 @@
2821 self.add_module("patch_999.py", patch_no_args_apply)
2822 try:
2823 self.patch_applier.apply_all()
2824- except BadPatchError, e:
2825+ except BadPatchError as e:
2826 self.assertTrue("mypackage/patch_999.py" in str(e))
2827 self.assertTrue("takes no arguments" in str(e))
2828 self.assertTrue("TypeError" in str(e))
2829@@ -360,7 +362,7 @@
2830 self.add_module("patch_999.py", patch_missing_apply)
2831 try:
2832 self.patch_applier.apply_all()
2833- except BadPatchError, e:
2834+ except BadPatchError as e:
2835 self.assertTrue("mypackage/patch_999.py" in str(e))
2836 self.assertTrue("no attribute" in str(e))
2837 self.assertTrue("AttributeError" in str(e))
2838@@ -375,7 +377,7 @@
2839 self.add_module("patch_999.py", "that's not python")
2840 try:
2841 self.patch_applier.apply_all()
2842- except BadPatchError, e:
2843+ except BadPatchError as e:
2844 self.assertTrue(" 999 " in str(e))
2845 self.assertTrue("SyntaxError" in str(e))
2846 else:
2847@@ -389,7 +391,7 @@
2848 self.add_module("patch_999.py", patch_name_error)
2849 try:
2850 self.patch_applier.apply_all()
2851- except BadPatchError, e:
2852+ except BadPatchError as e:
2853 self.assertTrue("mypackage/patch_999.py" in str(e))
2854 self.assertTrue("NameError" in str(e))
2855 self.assertTrue("blah" in str(e))
2856
2857=== modified file 'tests/schema/schema.py'
2858--- tests/schema/schema.py 2015-01-26 09:57:25 +0000
2859+++ tests/schema/schema.py 2017-06-09 20:57:05 +0000
2860@@ -18,6 +18,7 @@
2861 # You should have received a copy of the GNU Lesser General Public License
2862 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2863 #
2864+from builtins import object
2865 import os
2866 import sys
2867
2868@@ -65,9 +66,7 @@
2869 for name in list(sys.modules):
2870 if name in self._package_names:
2871 del sys.modules[name]
2872- elif filter(
2873- None,
2874- [name.startswith("%s." % x) for x in self._package_names]):
2875+ elif [_f for _f in [name.startswith("%s." % x) for x in self._package_names] if _f]:
2876 del sys.modules[name]
2877
2878 super(SchemaTest, self).tearDown()
2879
2880=== modified file 'tests/schema/sharding.py'
2881--- tests/schema/sharding.py 2014-12-18 11:37:22 +0000
2882+++ tests/schema/sharding.py 2017-06-09 20:57:05 +0000
2883@@ -18,6 +18,8 @@
2884 # You should have received a copy of the GNU Lesser General Public License
2885 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2886 #
2887+from builtins import range
2888+from builtins import object
2889 from tests.mocker import MockerTestCase
2890
2891 from storm.schema.schema import SchemaMissingError, UnappliedPatchesError
2892@@ -35,7 +37,7 @@
2893 if store.pristine:
2894 raise SchemaMissingError()
2895 if store.patch < self.patches:
2896- unapplied_versions = range(store.patch + 1, self.patches + 1)
2897+ unapplied_versions = list(range(store.patch + 1, self.patches + 1))
2898 raise UnappliedPatchesError(unapplied_versions)
2899
2900 def create(self, store):
2901
2902=== modified file 'tests/sqlobject.py'
2903--- tests/sqlobject.py 2016-03-01 11:44:05 +0000
2904+++ tests/sqlobject.py 2017-06-09 20:57:05 +0000
2905@@ -18,6 +18,7 @@
2906 # You should have received a copy of the GNU Lesser General Public License
2907 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2908 #
2909+import six
2910 import datetime
2911 import operator
2912
2913@@ -121,7 +122,7 @@
2914 _defaultOrder = "-Person.name"
2915 _table = "person"
2916 _idName = "name"
2917- _idType = unicode
2918+ _idType = six.text_type
2919 age = IntCol()
2920 ts = UtcDateTimeCol()
2921
2922@@ -1183,7 +1184,7 @@
2923 # properties:
2924 class Person(self.SQLObject):
2925 _idName = "name"
2926- _idType = unicode
2927+ _idType = six.text_type
2928 address = ForeignKey(foreignKey="Phone", dbName="address_id",
2929 notNull=True)
2930
2931
2932=== modified file 'tests/store/base.py'
2933--- tests/store/base.py 2016-05-13 18:55:24 +0000
2934+++ tests/store/base.py 2017-06-09 20:57:05 +0000
2935@@ -20,7 +20,11 @@
2936 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2937 #
2938
2939-from cStringIO import StringIO
2940+from future import standard_library
2941+standard_library.install_aliases()
2942+from builtins import range, object
2943+import six
2944+from io import StringIO
2945 import decimal
2946 import gc
2947 import operator
2948@@ -47,6 +51,7 @@
2949
2950 from tests.info import Wrapper
2951 from tests.helper import TestHelper
2952+from future.utils import with_metaclass
2953
2954
2955 class Foo(object):
2956@@ -1021,7 +1026,7 @@
2957 def test_find_max_unicode(self):
2958 title = self.store.find(Foo).max(Foo.title)
2959 self.assertEquals(title, "Title 30")
2960- self.assertTrue(isinstance(title, unicode))
2961+ self.assertTrue(isinstance(title, six.text_type))
2962
2963 def test_find_max_with_empty_result_and_disallow_none(self):
2964 class Bar(object):
2965@@ -1042,7 +1047,7 @@
2966 def test_find_min_unicode(self):
2967 title = self.store.find(Foo).min(Foo.title)
2968 self.assertEquals(title, "Title 10")
2969- self.assertTrue(isinstance(title, unicode))
2970+ self.assertTrue(isinstance(title, six.text_type))
2971
2972 def test_find_min_with_empty_result_and_disallow_none(self):
2973 class Bar(object):
2974@@ -1126,7 +1131,7 @@
2975 values = list(values)
2976 self.assertEquals(values, ["Title 30", "Title 20", "Title 10"])
2977 self.assertEquals([type(value) for value in values],
2978- [unicode, unicode, unicode])
2979+ [six.text_type, six.text_type, six.text_type])
2980
2981 def test_find_multiple_values(self):
2982 result = self.store.find(Foo).order_by(Foo.id)
2983@@ -1138,7 +1143,7 @@
2984
2985 def test_find_values_with_no_arguments(self):
2986 result = self.store.find(Foo).order_by(Foo.id)
2987- self.assertRaises(FeatureError, result.values().next)
2988+ self.assertRaises(FeatureError, next, result.values())
2989
2990 def test_find_slice_values(self):
2991 values = self.store.find(Foo).order_by(Foo.id)[1:2].values(Foo.id)
2992@@ -1552,7 +1557,7 @@
2993 result.group_by(FooValue.value2)
2994 result.order_by(Count(FooValue.id), Sum(FooValue.value1))
2995 result = list(result)
2996- self.assertEquals(result, [(2L, 2L), (2L, 2L), (2L, 3L), (3L, 6L)])
2997+ self.assertEquals(result, [(2, 2), (2, 2), (2, 3), (3, 6)])
2998
2999 def test_find_group_by_table(self):
3000 result = self.store.find(
3001@@ -2534,7 +2539,7 @@
3002 foo = self.store.get(Foo, 20)
3003 self.store.execute("UPDATE foo SET title='Title 40' WHERE id=20")
3004 self.store.reload(foo)
3005- for variable in get_obj_info(foo).variables.values():
3006+ for variable in list(get_obj_info(foo).variables.values()):
3007 self.assertFalse(variable.has_changed())
3008
3009 def test_reload_new(self):
3010@@ -4426,8 +4431,8 @@
3011 self.assertRaises(NoStoreError, foo2.bars.remove, object())
3012
3013 def test_string_reference(self):
3014- class Base(object):
3015- __metaclass__ = PropertyPublisherMeta
3016+ class Base(with_metaclass(PropertyPublisherMeta, object)):
3017+ pass
3018
3019 class MyBar(Base):
3020 __storm_table__ = "bar"
3021@@ -4453,8 +4458,8 @@
3022 metaclass. This makes it possible to work around problems with
3023 circular dependencies by delaying property resolution.
3024 """
3025- class Base(object):
3026- __metaclass__ = PropertyPublisherMeta
3027+ class Base(with_metaclass(PropertyPublisherMeta, object)):
3028+ pass
3029
3030 class MyFoo(Base):
3031 __storm_table__ = "foo"
3032@@ -4493,8 +4498,8 @@
3033 metaclass. This makes it possible to work around problems with
3034 circular dependencies by delaying resolution of the order by column.
3035 """
3036- class Base(object):
3037- __metaclass__ = PropertyPublisherMeta
3038+ class Base(with_metaclass(PropertyPublisherMeta, object)):
3039+ pass
3040
3041 class MyFoo(Base):
3042 __storm_table__ = "foo"
3043@@ -4918,7 +4923,7 @@
3044 foo = self.store.get(DictFoo, 20)
3045 foo["a"] = 1
3046
3047- self.assertEquals(foo.items(), [("a", 1)])
3048+ self.assertEquals(list(foo.items()), [("a", 1)])
3049
3050 new_obj = DictFoo()
3051 new_obj.id = 40
3052@@ -5252,7 +5257,7 @@
3053 self.store.add(foo)
3054 foo.id = AutoReload
3055 foo.title = u"New Title"
3056- self.assertTrue(isinstance(foo.id, (int, long)))
3057+ self.assertTrue(isinstance(foo.id, six.integer_types))
3058 self.assertEquals(foo.title, "New Title")
3059
3060 def test_autoreload_primary_key_doesnt_reload_everything_else(self):
3061@@ -5762,8 +5767,8 @@
3062 self.assertEquals(foo.title, "New Title")
3063
3064 def get_bar_proxy_with_string(self):
3065- class Base(object):
3066- __metaclass__ = PropertyPublisherMeta
3067+ class Base(with_metaclass(PropertyPublisherMeta, object)):
3068+ pass
3069
3070 class MyBarProxy(Base):
3071 __storm_table__ = "bar"
3072@@ -5930,8 +5935,8 @@
3073 self.store.commit()
3074 try:
3075 self.assertEquals(myfoo.title, title)
3076- except AssertionError, e:
3077- raise AssertionError(unicode(e, 'replace') +
3078+ except AssertionError as e:
3079+ raise AssertionError(six.text_type(e, 'replace') +
3080 " (ensure your database was created with CREATE DATABASE"
3081 " ... CHARACTER SET utf8)")
3082
3083@@ -6224,8 +6229,8 @@
3084 self.assertEquals(list(result), [])
3085
3086 def test_values_no_columns(self):
3087- self.assertRaises(FeatureError, list, self.result.values())
3088- self.assertRaises(FeatureError, list, self.empty.values())
3089+ self.assertRaises(FeatureError, list, list(self.result.values()))
3090+ self.assertRaises(FeatureError, list, list(self.empty.values()))
3091
3092 def test_values(self):
3093 self.assertEquals(list(self.result.values(Foo.title)), [])
3094
3095=== modified file 'tests/store/postgres.py'
3096--- tests/store/postgres.py 2012-03-08 13:46:23 +0000
3097+++ tests/store/postgres.py 2017-06-09 20:57:05 +0000
3098@@ -18,6 +18,7 @@
3099 # You should have received a copy of the GNU Lesser General Public License
3100 # along with this program. If not, see <http://www.gnu.org/licenses/>.
3101 #
3102+from builtins import object
3103 import os
3104 import gc
3105
3106
3107=== modified file 'tests/tracer.py'
3108--- tests/tracer.py 2012-06-28 14:21:49 +0000
3109+++ tests/tracer.py 2017-06-09 20:57:05 +0000
3110@@ -1,3 +1,5 @@
3111+from builtins import str
3112+from builtins import object
3113 import datetime
3114 import os
3115 import sys
3116@@ -290,7 +292,7 @@
3117
3118 try:
3119 self.execute()
3120- except TimeoutError, e:
3121+ except TimeoutError as e:
3122 self.assertEqual("0 seconds remaining in time budget", e.message)
3123 self.assertEqual(self.statement, e.statement)
3124 self.assertEqual(self.params, e.params)
3125@@ -662,7 +664,7 @@
3126 try:
3127 with CaptureTracer():
3128 raise RuntimeError("boom")
3129- except RuntimeError, error:
3130+ except RuntimeError as error:
3131 errors.append(error)
3132 [error] = errors
3133 self.assertEqual("boom", str(error))
3134
3135=== modified file 'tests/uri.py'
3136--- tests/uri.py 2008-01-30 13:03:27 +0000
3137+++ tests/uri.py 2017-06-09 20:57:05 +0000
3138@@ -18,6 +18,7 @@
3139 # You should have received a copy of the GNU Lesser General Public License
3140 # along with this program. If not, see <http://www.gnu.org/licenses/>.
3141 #
3142+from builtins import str
3143 from storm.uri import URI, URIError
3144
3145 from tests.helper import TestHelper
3146
3147=== modified file 'tests/variables.py'
3148--- tests/variables.py 2011-09-13 22:48:50 +0000
3149+++ tests/variables.py 2017-06-09 20:57:05 +0000
3150@@ -18,9 +18,13 @@
3151 # You should have received a copy of the GNU Lesser General Public License
3152 # along with this program. If not, see <http://www.gnu.org/licenses/>.
3153 #
3154+from future import standard_library
3155+standard_library.install_aliases()
3156+from builtins import str, object
3157+import six
3158 from datetime import datetime, date, time, timedelta
3159 from decimal import Decimal
3160-import cPickle as pickle
3161+import pickle as pickle
3162 import gc
3163 import weakref
3164 try:
3165@@ -143,7 +147,7 @@
3166 variable = CustomVariable(allow_none=False, column=column)
3167 try:
3168 variable.set(None)
3169- except NoneError, e:
3170+ except NoneError as e:
3171 pass
3172 self.assertTrue("column_name" in str(e))
3173
3174@@ -152,7 +156,7 @@
3175 variable = CustomVariable(allow_none=False, column=column)
3176 try:
3177 variable.set(None)
3178- except NoneError, e:
3179+ except NoneError as e:
3180 pass
3181 self.assertTrue("table_name.column_name" in str(e))
3182
3183@@ -467,7 +471,7 @@
3184 self.assertEquals(variable.get(), epoch)
3185 variable.set(0.0)
3186 self.assertEquals(variable.get(), epoch)
3187- variable.set(0L)
3188+ variable.set(0)
3189 self.assertEquals(variable.get(), epoch)
3190 variable.set(epoch)
3191 self.assertEquals(variable.get(), epoch)
3192@@ -475,7 +479,7 @@
3193
3194 def test_get_set_from_database(self):
3195 datetime_str = "1977-05-04 12:34:56.78"
3196- datetime_uni = unicode(datetime_str)
3197+ datetime_uni = six.text_type(datetime_str)
3198 datetime_obj = datetime(1977, 5, 4, 12, 34, 56, 780000)
3199
3200 variable = DateTimeVariable()
3201@@ -488,7 +492,7 @@
3202 self.assertEquals(variable.get(), datetime_obj)
3203
3204 datetime_str = "1977-05-04 12:34:56"
3205- datetime_uni = unicode(datetime_str)
3206+ datetime_uni = six.text_type(datetime_str)
3207 datetime_obj = datetime(1977, 5, 4, 12, 34, 56)
3208
3209 variable.set(datetime_str, from_db=True)
3210@@ -553,7 +557,7 @@
3211
3212 def test_get_set_from_database(self):
3213 date_str = "1977-05-04"
3214- date_uni = unicode(date_str)
3215+ date_uni = six.text_type(date_str)
3216 date_obj = date(1977, 5, 4)
3217 datetime_obj = datetime(1977, 5, 4, 0, 0, 0)
3218
3219@@ -597,7 +601,7 @@
3220
3221 def test_get_set_from_database(self):
3222 time_str = "12:34:56.78"
3223- time_uni = unicode(time_str)
3224+ time_uni = six.text_type(time_str)
3225 time_obj = time(12, 34, 56, 780000)
3226
3227 variable = TimeVariable()
3228@@ -610,7 +614,7 @@
3229 self.assertEquals(variable.get(), time_obj)
3230
3231 time_str = "12:34:56"
3232- time_uni = unicode(time_str)
3233+ time_uni = six.text_type(time_str)
3234 time_obj = time(12, 34, 56)
3235
3236 variable.set(time_str, from_db=True)
3237@@ -667,7 +671,7 @@
3238
3239 def test_get_set_from_database(self):
3240 delta_str = "42 days 12:34:56.78"
3241- delta_uni = unicode(delta_str)
3242+ delta_uni = six.text_type(delta_str)
3243 delta_obj = timedelta(days=42, hours=12, minutes=34,
3244 seconds=56, microseconds=780000)
3245
3246@@ -681,7 +685,7 @@
3247 self.assertEquals(variable.get(), delta_obj)
3248
3249 delta_str = "1 day, 12:34:56"
3250- delta_uni = unicode(delta_str)
3251+ delta_uni = six.text_type(delta_str)
3252 delta_obj = timedelta(days=1, hours=12, minutes=34, seconds=56)
3253
3254 variable.set(delta_str, from_db=True)
3255@@ -769,7 +773,7 @@
3256 def test_unsupported_unit(self):
3257 try:
3258 self.check("1 month", None)
3259- except ValueError, e:
3260+ except ValueError as e:
3261 self.assertEquals(str(e), "Unsupported interval unit 'month' "
3262 "in interval '1 month'")
3263 else:
3264@@ -778,7 +782,7 @@
3265 def test_missing_value(self):
3266 try:
3267 self.check("day", None)
3268- except ValueError, e:
3269+ except ValueError as e:
3270 self.assertEquals(str(e), "Expected an interval value rather than "
3271 "'day' in interval 'day'")
3272 else:
3273@@ -916,7 +920,7 @@
3274 # simplejson/json.
3275 variable = self.variable_type()
3276 variable.set({u"a": 1})
3277- self.assertTrue(isinstance(variable.get(to_db=True), unicode))
3278+ self.assertTrue(isinstance(variable.get(to_db=True), six.text_type))
3279
3280
3281 class ListVariableTest(TestHelper):
3282
3283=== modified file 'tests/wsgi.py'
3284--- tests/wsgi.py 2012-06-04 14:14:50 +0000
3285+++ tests/wsgi.py 2017-06-09 20:57:05 +0000
3286@@ -1,4 +1,7 @@
3287-import Queue
3288+from future import standard_library
3289+standard_library.install_aliases()
3290+from builtins import object
3291+import queue
3292 from unittest import TestCase
3293 import threading
3294 import time
3295@@ -65,7 +68,7 @@
3296 # with two threads in a request at once, each only sees their own
3297 # timeline.
3298 app, find_timeline = make_app(self.stub_app)
3299- errors = Queue.Queue()
3300+ errors = queue.Queue()
3301 sync = threading.Condition()
3302 waiting = []
3303 def check_timeline():
3304
3305=== modified file 'tests/zope/adapters.py'
3306--- tests/zope/adapters.py 2009-03-05 21:53:12 +0000
3307+++ tests/zope/adapters.py 2017-06-09 20:57:05 +0000
3308@@ -18,6 +18,7 @@
3309 # You should have received a copy of the GNU Lesser General Public License
3310 # along with this program. If not, see <http://www.gnu.org/licenses/>.
3311 #
3312+from builtins import object
3313 from tests.helper import TestHelper
3314 from tests.zope import has_zope_component
3315
3316
3317=== modified file 'tests/zope/testing.py'
3318--- tests/zope/testing.py 2015-05-11 08:47:32 +0000
3319+++ tests/zope/testing.py 2017-06-09 20:57:05 +0000
3320@@ -18,6 +18,7 @@
3321 # You should have received a copy of the GNU Lesser General Public License
3322 # along with this program. If not, see <http://www.gnu.org/licenses/>.
3323 #
3324+from builtins import object
3325 import os
3326 import sys
3327
3328@@ -237,7 +238,7 @@
3329 real_invalidate = store.invalidate
3330
3331 def invalidate_proxy():
3332- self.assertEqual(0, len(store._alive.values()))
3333+ self.assertEqual(0, len(list(store._alive.values())))
3334 real_invalidate()
3335 store.invalidate = invalidate_proxy
3336
3337
3338=== modified file 'tests/zope/zstorm.py'
3339--- tests/zope/zstorm.py 2012-03-06 10:28:06 +0000
3340+++ tests/zope/zstorm.py 2017-06-09 20:57:05 +0000
3341@@ -18,6 +18,9 @@
3342 # You should have received a copy of the GNU Lesser General Public License
3343 # along with this program. If not, see <http://www.gnu.org/licenses/>.
3344 #
3345+from future import standard_library
3346+standard_library.install_aliases()
3347+from builtins import range
3348 import threading
3349 import weakref
3350 import gc
3351@@ -355,7 +358,7 @@
3352 store.execute("SELECT 1")
3353 except ZStormError:
3354 failures.append("ZStormError raised")
3355- except Exception, exc:
3356+ except Exception as exc:
3357 failures.append("Expected ZStormError, got %r" % exc)
3358 else:
3359 failures.append("Expected ZStormError, nothing raised")

Subscribers

People subscribed via source and target branches

to status/vote changes: