Skip to content

Loading…

Porting to Python 3, part 4 ("final") #558

Merged
merged 17 commits into from

4 participants

@vperic

This is, tentatively, the final pull request for the GSoC project. There are a few remaining issues (the Issues page is down now so I'll add the numbers when it's up), but I feel Python 3 support is mostly here. I will definitely be sticking around to keep working on this (and PyPy compatibility), as problems will probably arise once people actually use SymPy with Python 3. Notably, this adds a bin/use2to3 script, which will create a "sympy-py3k" directory with Python 3-compatible code in it (see that commit and the script itself for more info). This should make testing this easier and I'd appreciate it if people could test it under Python 3 too and report any issues they might get (that aren't already known). Note, Aaron asked me to refactor this script to use git directly and I'll do that tomorrow; I just wanted to get the initial discussion going on the other commits as it's currently quite useable too.

I've tested this and it passes fine under Python 2, and only the errors already on the issue tracker for Python 3.

@vperic

And the issue tracker is alive again. So, this fixes issue 2605, 2608, 2610, 2612 (this should be noted in the relevant commits, too). The remaining issues are 2574 (Wrong assumptions for LambertW(-1) ), 2609 (core/test_priority error under Python 3 ), 2611 (tensor/index_methods errors in Python 3 ). These equate to two failures when running tests and one failure for the doctests (actually 3 separate issues but all in one file). Anything else is "unknown".

@asmeurer
SymPy member

Make a note of this pull request on all those issues, please.

@asmeurer
SymPy member

Once again, very good commit messages.

@asmeurer
SymPy member

Other than my comments, the changes look good. I still need to play around with the 2to3 script, and also run the tests.

@vperic

I've rebased to address Aaron's comments (and of course, lost them). They were all mostly minor, though, and I replied to each one before rebasing (so Aaron will get the mails and can confirm it was fixed). Of course, any further comments/testing is appreciated.

I've also ran the tests under Python 3.1 and the output is the same as for 3.2 (minus the doctest differences, as noted in [this][1] discussion), so that's good. I'd still really like it if someone could test this under Windows (and Mac too, but that is more likely to be fine). I don't really expect problems, but there could be something related to the printers or somesuch.

[1] vperic@404e19d

EDIT: Actually, I just noticed a large bit of code got in which produce a couple of trivial merge conflicts, so I've rebased the whole pull request on top of latest master.

@vperic

Oh, note that there's a whitespace code quality failure under Python 3 code. This is a change done by 2to3 and I haven't found a way to work around it. I've reported this upstream1 but there hasn't been any response.

[1] http://bugs.python.org/issue12771

@smichr smichr commented on the diff
doc/src/modules/plotting.txt
@@ -150,13 +150,12 @@ gray-scale because we have applied the default color gradient uniformly for
each color component. When defining a color scheme in this way, you might want
to supply a color gradient as well:
- >>> p[1].color = (Fx**2 + Fy**2 + Fz**2)**(0.5),
- ................ (0.1,0.1,0.9), (0.9,0.1,0.1)
+ >>> p[1].color = (Fx**2 + Fy**2 + Fz**2)**(0.5), (0.1,0.1,0.9), (0.9,0.1,0.1)
@smichr SymPy member
smichr added a note

to avoid the long line can this be

>>> p[1].color = (Fx**2 + Fy**2 + Fz**2)**(0.5),
... (0.1,0.1,0.9), (0.9,0.1,0.1)
@vperic
vperic added a note

Actually, the line is just one character over 80 so I think it's fine. I can change it though, it's not a problem.

@smichr SymPy member
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@smichr smichr commented on an outdated diff
doc/src/tutorial.txt
@@ -146,7 +146,7 @@ and the denominator, so Rational(1,2) represents 1/2, Rational(5,2) 5/2 and so o
proceed with caution while working with python int's since they truncate
integer division, and that's why::
@smichr SymPy member
smichr added a note

how about updating the text and example to say something like

...since in version 3, the ratio of two ints is a Python float (not a SymPy Float)

``python

1/2 #doctest: +SKIP
0.5

and in previous versions (unless division is imported from future) the ratio of two ints is a truncated int

>>> 1/2 #doctest: +SKIP
0

In neither case is a SymPy Number returned.

@vperic
vperic added a note

Ok, I'll add another commit (as this is going a bit beyond the scope of the current changes) with this. I'm not sure how to phrase this exactly so I'm just going to copy what you used; if you have a better idea you can push it somewhere and I'll cherry-pick it onto my branch.

@smichr SymPy member
@vperic
vperic added a note

Great, thanks! You can just push the change somewhere and I'll cherry-pick it from you, you don't need to bother with the pull request machinery.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@smichr smichr commented on an outdated diff
doc/src/tutorial.txt
((8 lines not shown))
-So that things pretty print. See the :ref:`printing-tutorial` section below. If you have
-a unicode font installed, your output may look a little different (it will look slightly
-nicer).
+So that things pretty print. See the :ref:`printing-tutorial` section below.
@smichr SymPy member
smichr added a note

'This will make things look pretty when printed.'

@vperic
vperic added a note

Ok. Changed that first sentance to "This will make things look better when printed." (I feel that sounds, well, better, than using "pretty").

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@smichr smichr commented on an outdated diff
sympy/ntheory/factor_.py
((9 lines not shown))
>>> set([igcd(pow(a, M, n) - 1, n) for a in range(2, 256) if
... igcd(pow(a, M, n) - 1, n) != n])
set([1009])
But does aM % d for every divisor of n give 1?
- >>> aM = pow(a, M, n)
+ >>> aM = pow(256, M, n)
@smichr SymPy member
smichr added a note

Does the loop variable go to the last indicated value in range in python 3? Because in 2.7 the value of a at the end of the loop is 255. So to be explicit (and consistent with intent) that 256 should be 255 in L463

@vperic
vperic added a note

No, the behaviour is the same, good catch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@vperic

Thanks for the review, Chris. I've incorporated the changes in the relevant commits but I'll hold off on pushing for the moment in case someone else wants to weight in on the wording for the tutorial or something else.

Also, you run Windows I believe? Would you mind testing the use2to3 script? It should work but rather safe than sorry. It should work with Python 2.7. Of course, it'd also be great if you could run the tests under Python 3 too, but I understand that it can be a chore. Thanks in any case!

@smichr
SymPy member
@asmeurer
SymPy member

Regarding the whitespace issue, that will likely take forever to fix upstream, so for now, let's just have use2to3 run the strip_whitespace utility.

vperic added some commits
@vperic vperic utilities/runtests: Fix doctest runner for Python 3
In Python 3, the _load_testfile method has a fourth argument, encoding.
As we already use this, just pass it to the function when under Python
3. However, we then try to decode a str (because we specified
an encoding) which is wrong, so set the encoding to None in Python 3. We
cannot just use None as an encoding as Python 2 relies on the decode
step.

This error only appeared when running .txt doctests, as the usual
doctests use a different codepath.
8b4bb4c
@vperic vperic doc/src/gotchas: Make type() doctest Python 3 compatible
In Python 2, type(float) returns "<type 'float'>", but in Python 3 it
returns "<class 'float'>". This confuses the doctest which must match
output exactly. Use the ellipsis syntax to avoid this. See also commit
b457bee: Make some doctests Python 3 compatible.
adbc076
@vperic vperic doc/../evalf.txt: Skip some doctests related to float()
In Python 3, floats have a higher precision by default and it is not
possible to override it. This is a problem for doctests, which rely on
matching output exactly. As the purpose of these doctests is to show
limitations in Python floats, we can't just convert them to the more
precise SymPy Floats. Fix by skipping the four affected doctests.
56c3969
@vperic vperic utilities/runtest: Wrap a str.encode with a version check
Commit f75a101 (Fix the doctest runner in Python 2.5 with utf-8 files)
introduced the _indent method from upstream. In Python 3, this converts
a (unicode) string to bytes, which don't support regex, thus crashing
when there's a doctest failure (that's the only time this function is
called). Wrap the encode code with a version check and enter it only
under Python 2.
d12ea8f
@vperic vperic ntheory/factor_: Don't use reduce in doctest
2to3 crashes when ran on a doctest containing reduce(). This is
an upstream problem (issue 12611). Replace the only use of this in our
doctests with a for cycle. This fixes issue #2605.
07f343b
@vperic vperic Fix 2to3 TokenError parsing errors
2to3 reports some errors of the type "TokenError: ('EOF in multi-line
statement', ...)" and can't parse those docstrings. Fix these by
correctly using multi-line strings and statements in doctests.
b239fc2
@vperic vperic physics/wigner: Explicitly cast range() arguments to int
In Python 2, a SymPy Float could be used as an argument of range(). This
is no longer possible in Python 3. There are two occurances of this in
wigner.py. In the first, a function which expected an integer (per the
doctest) got a Float and then called range() on it; in the second,
range() was called directly on Floats. Fix by explicitly casting to int.
7f7b86c
@vperic vperic ntheory/factor_: Fix namespace leak from list comprehension
In Python 3, variables are no longer in the namespace after being used
in a list comprehension. Fix one doctest to directly use a number
instead of a variable.
55e0bf1
@vperic vperic doc/src/*.txt: Use init_printing() instead of setting the displayhook
Some .txt doctests were setting the sys.displayhook to pprint which
produced subtle errors in Python 3 (printing an extra "None" in some
cases). Instead, use the init_printing() function from
interactive.printing. It is called by default with use_unicode=false,
to match the expected output in the files.

These errors only appeared in tutorial.txt and matrices.txt, but use
init_printing() in four other files which were also setting the
displayhook.

Also skip a doctest in the tutorial demonstrating the old-style
division which no longer exists in Python 3 and modify two doctests in
matrices.txt not to expect a result of None (which wasn't strictly
correct).
0319a91
@vperic vperic printing/printer: Raise AttributeError in a property
The hasattr() function is defined to call getattr() and see if an
exception is raised (in which case the attribute doesn't exist). This
has been changed in Python 3.2 to catch only AttributeErrors and not
all kinds of exceptions. See also issue #2608.

A property in printer.py checked for a key and so could raise a
KeyError, which now wouldn't be caught by hasattr(). Refactor to raise
an AttributeError if the key doesn't exist. This bug was caught by a
test in utilities/tests/test_pickling.
1dec4c5
@vperic vperic matrices.py: Raise AttributeError in a property
The hasattr() function is defined to call getattr() and see if an
exception is raised (in which case the attribute doesn't exist). This
has been changed in Python 3.2 to catch only AttributeErrors and not all
kinds of exceptions. See also issue #2608 and the previous commit.

The property D ("Dirac conjugation") could raise a ShapeError, which now
wouldn't be caught by hasattr(). Wrap the matrix multiplication with a
try/except statement that catches ShapeError and returns AttributeError
instead. Also change a relevant test to expect an AttributeError now.
f732c4e
@vperic vperic Add bin/use2to3 script
SymPy needs a 2to3 run to be usable in Python 3. Unfortunately, running
2to3 on mpmath (which is already compatible) produces garbage code. To
avoid this, we first copy all files (except those from mpmath) to a
"sympy-py3k" directory, run 2to3 on them and then copy over the files
from mpmath. One can then use SymPy normally from the sympy-py3k
directory under Python 3. As running 2to3 is a slow process, we try to
note which files have been updated since the last run and only process
those. Also add "sympy-py3k" to .gitignore.

Finally, this is a first draft of the script. As such, there are several
TODOs which note areas of possible further improvement.
6121523
@vperic vperic combinatorics/permutations: Use floor division
Some code was using the ambiguous division operator on integers. This
produces floats by default in Python 3 (1.0 as opposed to 1) which
cannot be used as list indices. Fix by using floor division (//) where
appropriate. A doctest was also failing, it expected an implicit cast to
int (1.0 to 1 automatically).

Also remove some redundant casts to int, factorial() returns an int
always.
49f0686
@vperic vperic utilities/runtest: Make findout_terminal_width Python 3 compatible
Commit 76d0046 added a function, findout_terminal_width. This function
relies on regexing the stdout, which is of type str in Python 2. In
Python 3 it is a 'bytes' object and it's not possible to regex it, which
then crashes the doctest runner. Fix by checking if we are under Python
3 and decoding from "utf-8" if that is the case.
b0edd91
@vperic vperic quantum/state: Use floor division in doctest
A doctest was using the ambiguous division operator (/). Because the
expected result was floor division, use the unambiguous floor division
operator (//). This removes errors under Python 3 and fixes issue #2610.
055a166
@vperic vperic quantum/__init__: Change imports syntax for Python 3 compatibility
The __init__.py file for quantum uses some importing magic so that both
"from quantum import *" and "from quantum import State" to work. This
fails under Python 3 because some underlying rules seem to have changed.
The likely cause is that the module names shadow function names (or
vice-versa). Fix by directly import __all__ from the affected modules. A
better fix might be to change the module names, but this is a
backward-incompatible change and this workaround is equivalent
functionally.
300e926
@vperic

Ok. I've rebased again, changed one commit to skip doctests rather than use the ellipsis syntax (per Aaron's comments here[1]). We were also talking about the use2to3 script, you can see the discussion here2, but these changes could go in later too.

I've also added one more commit from Chris improving the tutorial.txt file a bit. @smichr, I edited your commit message to be a bit more informative, I hope it's ok? You also seem to have based your work on an older branch (porting4-2to3 as opposed to porting 4; yes, the names are a bit unfortunate) so there were a few conflicts but I think I did it right.

I'm going on a trip and I won't be able to edit anything for some time, so I'd prefer it if this could go in right now (@rlamy, if you've got the time, I think your ack would be enough to have this in). The improvements to use2to3 ([2] and others) can be done later.

[1] vperic@3100c28#commitcomment-537246
[2] vperic@7de283c#commitcomment-537275

@smichr smichr doc/src/tutorial: Misc improvements to the documentation
Improve the explanation of the difference between SymPy numbers and
Python numbers and other small changes.
b448dbe
@smichr
SymPy member

Aaron said it looked fine; I gave it a once over and made some comments; you said that the whitespace thing can wait, so I'm going to push this if all tests pass.

@smichr
SymPy member

ok tests pass under 2.7; I'll try in 3.2

@smichr
SymPy member

Hmmm...when I start python 3.2 I can't import sympy:

>>> import sympy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "sympy\__init__.py", line 20, in <module>
    from sympy.core import *
  File "sympy\core\__init__.py", line 4, in <module>
    from sympify import sympify
ImportError: No module named sympify
@vperic

Right, you need to use the "use2to3" script. This will create a "sympy-py3k" directory that's a copy of the whole sympy structure, just Python 3 compatible; you can then use sympy from that directory normally. This of course needs to be mentioned in README and other places too, for now lets consider Python 3 support a "developer" feature.

@rlamy
SymPy member

Actually, I get the same problem even when I make sure that python3 does import the converted version. For instance:

ronan@ronan-desktop:~/Projets/sympy-git/sympy-py3k$ python3 bin/doctest 
Traceback (most recent call last):
  File "bin/doctest", line 51, in <module>
    import sympy
  File "/media/sda2/Boulot/Projets/sympy-git/sympy-py3k/sympy/__init__.py", line 20, in <module>
    from sympy.core import *
  File "/media/sda2/Boulot/Projets/sympy-git/sympy-py3k/sympy/core/__init__.py", line 4, in <module>
    from sympify import sympify
ImportError: No module named sympify

It seems that use2to3 doesn't convert implicit relative imports (e.g. from sympify import *) to explicit ones (i.e. from .sympify import *).

Apart from that, everything looks good.

@vperic

Huh, that's strange. So it doesn't work at all for you in Python 3? The "import" filter should be getting ran automatically (and is for me). What happens if you run it manually in the sympy-py3k directory, does that make it work ("2to3 -w -n -f import ./")?

@rlamy
SymPy member

This was apparently just a fluke. My first bin/use2to3 run missed some of the files. I deleted sympy-py3k/ and tried again and now it works.

@asmeurer
SymPy member

Good thing, because from .module import * doesn't work in Python 2.5.

It does sound like there might be a bug in use2to3 though.

By the way, If all my comments have been addressed and tests pass everywhere, I am +1 to merge.

@asmeurer
SymPy member

So I finally got around to testing this. Some comments:

  • I still think that the shebang line for use2to3 should be /usr/bin/env python3. That way, I can just type ./bin/use2to3, and it will do the right thing.

  • The tool gave me the following warnings/errors, which I'm assuming you know about


RefactoringTool: Warnings/messages while refactoring:
RefactoringTool: ### In file sympy-py3k/./doc/src/modules/polys/wester.txt ###
RefactoringTool: Line 396: You should use a for loop here
RefactoringTool: There were 2 errors:
RefactoringTool: Can't parse docstring in sympy-py3k/./sympy/utilities/codegen.py line 922: ParseError: bad token: type=55, value=' ', context=('', (922, 40))
RefactoringTool: Can't parse docstring in sympy-py3k/./doc/src/gotchas.txt line 415: ParseError: bad input: type=1, value='x', context=('', (415, 1))
  • If I run isympy (in Python 3), I get:
Traceback (most recent call last):
  File "./bin/isympy", line 163, in <module>
    main()
  File "./bin/isympy", line 159, in main
    from sympy.interactive import init_session
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/__init__.py", line 20, in <module>
    from sympy.core import *
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/core/__init__.py", line 4, in <module>
    from .sympify import sympify
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/core/sympify.py", line 5, in <module>
    from .core import all_classes as sympy_classes
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/core/core.py", line 2, in <module>
    from sympy.core.compatibility import cmp
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/core/compatibility.py", line 133, in <module>
    import builtins
ImportError: No module named built-ins

This is assumedly because it's still running in Python 2. Therefore, I think the tool should change all shebang lines that it encounters to /usr/bin/env python3.

  • Here are the test and doctest failures I got in Python 3.2, in case there's anything new to you:

Doctests

File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/tensor/index_methods.py", line 298, in sympy.tensor.index_methods.get_contraction_structure
Failed example:
    get_contraction_structure(x[i]*y[i] + A[j, j])
Expected:
    {(i,): set([x[i]*y[i]]), (j,): set([A[j, j]])}
Got:
    {(j,): set([A[j, j]]), (i,): set([x[i]*y[i]])}
**********************************************************************
File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/tensor/index_methods.py", line 311, in sympy.tensor.index_methods.get_contraction_structure
Failed example:
    sorted(d[x[i, i]*y[j, j]])  # factors are contracted ``first''
Exception raised:
    Traceback (most recent call last):
      File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/doctest.py", line 1253, in __run
        compileflags, 1), test.globs)
      File "<doctest sympy.tensor.index_methods.get_contraction_structure[10]>", line 1, in <module>
        sorted(d[x[i, i]*y[j, j]])  # factors are contracted ``first''
    TypeError: unorderable types: dict() < dict()
**********************************************************************
File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/tensor/index_methods.py", line 324, in sympy.tensor.index_methods.get_contraction_structure
Failed example:
    d[x[i]*(A[i, j]*x[j] + y[i])]
Exception raised:
    Traceback (most recent call last):
      File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/doctest.py", line 1253, in __run
        compileflags, 1), test.globs)
      File "<doctest sympy.tensor.index_methods.get_contraction_structure[14]>", line 1, in <module>
        d[x[i]*(A[i, j]*x[j] + y[i])]
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/interactive/printing.py", line 17, in displayhook
        print(stringify_func(arg))
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/printing/str.py", line 517, in sstrrepr
        s = p.doprint(expr)
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/printing/printer.py", line 227, in doprint
        return self._str(self._print(expr))
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/printing/printer.py", line 251, in _print
        return getattr(self, printmethod)(expr, *args)
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/printing/str.py", line 189, in _print_list
        return "[%s]"%self.stringify(expr, ", ")
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/printing/str.py", line 30, in stringify
        return sep.join([self.parenthesize(item, level) for item in args])
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/printing/str.py", line 30, in <listcomp>
        return sep.join([self.parenthesize(item, level) for item in args])
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/printing/str.py", line 27, in parenthesize
        return self._print(item)
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/printing/printer.py", line 251, in _print
        return getattr(self, printmethod)(expr, *args)
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/printing/str.py", line 82, in _print_dict
        keys.sort( key=cmp_to_key(Basic.compare_pretty) )
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/core/compatibility.py", line 119, in __lt__
        return mycmp(self.obj, other.obj) < 0
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/core/basic.py", line 262, in compare_pretty
        return cmp(a,b)
      File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/core/compatibility.py", line 137, in cmp
        return (a > b) - (a < b)
    TypeError: unorderable types: NoneType() > tuple()

Regular Tests

  File "/users/aaronmeurer/documents/python/sympy/sympy/sympy-py3k/sympy/solvers/tests/test_solvers.py", line 230, in test_tsolve
    assert solve(3*x+5+2**(-5*x+3), x) in [
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/solvers/solvers.py", line 563, in solve
    solution = _solve(f, *symbols, **flags)
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/solvers/solvers.py", line 861, in _solve
    result = list(map(simplify, result))
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/simplify/simplify.py", line 1647, in simplify
    expr1 = cancel(powsimp(expr))
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/simplify/simplify.py", line 1218, in powsimp
    return Add(*[powsimp(t, deep, combine, force) for t in expr.args])
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/simplify/simplify.py", line 1218, in <listcomp>
    return Add(*[powsimp(t, deep, combine, force) for t in expr.args])
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/simplify/simplify.py", line 1262, in powsimp
    bpos = b.is_positive
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/core/assumptions.py", line 155, in getit
    return self._what_known_about(fact)
  File "/Users/aaronmeurer/Documents/python/sympy/sympy/sympy-py3k/sympy/core/assumptions.py", line 383, in _what_known_about
    a = _real_cmp0_table[k][cmp(self.evalf(), 0)]
KeyError: (0 < 6.10079316127918 + 2.7219358435534*I) - (6.10079316127918 + 2.7219358435534*I < 0)

____________________________________________________________________________________________________________________________________________________________________________________________________________
_____________________________________________________________________________ sympy/core/tests/test_arit.py:test_Add_primitive _____________________________________________________________________________
  File "/users/aaronmeurer/documents/python/sympy/sympy/sympy-py3k/sympy/core/tests/test_arit.py", line 1190, in test_Add_primitive
    assert (3*x + 6).primitive() == (3, x + 2)
AssertionError
____________________________________________________________________________________________________________________________________________________________________________________________________________
_______________________________________________________________________________ sympy/core/tests/test_exprtools.py:test_Term _______________________________________________________________________________
  File "/users/aaronmeurer/documents/python/sympy/sympy/sympy-py3k/sympy/core/tests/test_exprtools.py", line 72, in test_Term
    assert Term((2*x + 2)*(3*x + 6)**2) == Term(18, Factors({x + 1: 1, x + 2: 2}), Factors({}))
AssertionError
____________________________________________________________________________________________________________________________________________________________________________________________________________
____________________________________________________________________________ sympy/core/tests/test_exprtools.py:test_gcd_terms _____________________________________________________________________________
  File "/users/aaronmeurer/documents/python/sympy/sympy/sympy-py3k/sympy/core/tests/test_exprtools.py", line 77, in test_gcd_terms
    assert _gcd_terms(f) == ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1)
AssertionError
____________________________________________________________________________________________________________________________________________________________________________________________________________
_______________________________________________________________________________ sympy/core/tests/test_match.py:test_complex ________________________________________________________________________________
  File "/users/aaronmeurer/documents/python/sympy/sympy/sympy-py3k/sympy/core/tests/test_match.py", line 123, in test_complex
    assert (a+b*I).match(x+y*I) == {x : a, y : b}
AssertionError
____________________________________________________________________________________________________________________________________________________________________________________________________________
________________________________________________________________________________ sympy/core/tests/test_priority.py:test_div ________________________________________________________________________________
  File "/users/aaronmeurer/documents/python/sympy/sympy/sympy-py3k/sympy/core/tests/test_priority.py", line 88, in test_div
    assert l/h == h/l == 'high'
AssertionError
____________________________________________________________________________________________________________________________________________________________________________________________________________
________________________________________________________________ sympy/utilities/tests/test_code_quality.py:test_whitespace_and_exceptions _________________________________________________________________
  File "/users/aaronmeurer/documents/python/sympy/sympy/sympy-py3k/sympy/utilities/tests/test_code_quality.py", line 110, in test_whitespace_and_exceptions
    check_directory_tree(SYMPY_PATH, test, exclude)
  File "/users/aaronmeurer/documents/python/sympy/sympy/sympy-py3k/sympy/utilities/tests/test_code_quality.py", line 64, in check_directory_tree
    file_check(fname)
  File "/users/aaronmeurer/documents/python/sympy/sympy/sympy-py3k/sympy/utilities/tests/test_code_quality.py", line 86, in test
    assert False, message_space % (fname, idx+1)
AssertionError: File contains trailing whitespace: /users/aaronmeurer/documents/python/sympy/sympy/sympy-py3k/sympy/ntheory/factor_.py, line 320.
  • When this gets in, see if my recent fix to the test runner makes the skip messages appear correctly when you run the tests with -v in Python 3. Ditto for listing XPASSed tests at the end.

  • Regarding that last failure, like I said, just run the strip_whitepace utility in use2to3.

@asmeurer
SymPy member

When this gets in, see if my recent fix to the test runner makes the skip messages appear correctly when you run the tests with -v in Python 3. Ditto for listing XPASSed tests at the end.

I guess this change is already part of this branch, but it doesn't work. So this will have to be looked into.

@rlamy
SymPy member

These errors are known. I'm not sure how the shebang lines should be handled, 2to3 doesn't change them, apparently, but I don't know the rationale for that behaviour. In any case, explicitly invoking the scripts with python3 (e.g. python3 bin/doctest) should work everywhere. I'd say that we should worry about it later on and merge it as is.

I'd still like to get confirmation that it works OK on Windows before merging this in though. @smichr?

And BTW, the relative import syntax should work in 2.5.

@asmeurer
SymPy member

Right. It's mostly a convenience thing so that you can type ./sympy-py3k/bin/isympy instead of python3 sympy-py3k/bin/isympy. If you don't have python3 in your env PATH, I don't know how you plan to open Python 3 anyway, but it's trivial to add it, or to call it the hard way if you don't want to. In my experience, 99% of Python 3 installations will add a python3 shortcut.

But I do agree that this can go in without my comments being fixed. Just make sure that they are mentioned in the issues.

And BTW, the relative import syntax should work in 2.5.

Vlad and I discussed this a while back. Apparently due to some bug, relative import * does not work in Python 2.5 (I hope I remember this correctly), and since Python 2.5 is in security fix only mode, it can't be fixed.

@rlamy
SymPy member

I found a Windows system to test this. Python3 conversion and tests seem to work, so I'm pushing this in.

Vlad and I discussed this a while back. Apparently due to some bug, relative import * does not work in Python 2.5 (I hope I remember this correctly), and since Python 2.5 is in security fix only mode, it can't be fixed.

OK.

@rlamy rlamy merged commit 77c3a74 into sympy:master
@vperic

Thanks Ronan, everyone. I've closed the relevant issues on the tracker. I've also opened an issue for the requested improvements to bin/use2to3 [1]. If there is something I missed, please comment. I also plan to improve the documentation (so that users actually know that Python 3 is supported), but didn't open an issue for it.

[1] http://code.google.com/p/sympy/issues/detail?id=2673

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 19, 2011
  1. @vperic

    utilities/runtests: Fix doctest runner for Python 3

    vperic committed
    In Python 3, the _load_testfile method has a fourth argument, encoding.
    As we already use this, just pass it to the function when under Python
    3. However, we then try to decode a str (because we specified
    an encoding) which is wrong, so set the encoding to None in Python 3. We
    cannot just use None as an encoding as Python 2 relies on the decode
    step.
    
    This error only appeared when running .txt doctests, as the usual
    doctests use a different codepath.
  2. @vperic

    doc/src/gotchas: Make type() doctest Python 3 compatible

    vperic committed
    In Python 2, type(float) returns "<type 'float'>", but in Python 3 it
    returns "<class 'float'>". This confuses the doctest which must match
    output exactly. Use the ellipsis syntax to avoid this. See also commit
    b457bee: Make some doctests Python 3 compatible.
  3. @vperic

    doc/../evalf.txt: Skip some doctests related to float()

    vperic committed
    In Python 3, floats have a higher precision by default and it is not
    possible to override it. This is a problem for doctests, which rely on
    matching output exactly. As the purpose of these doctests is to show
    limitations in Python floats, we can't just convert them to the more
    precise SymPy Floats. Fix by skipping the four affected doctests.
  4. @vperic

    utilities/runtest: Wrap a str.encode with a version check

    vperic committed
    Commit f75a101 (Fix the doctest runner in Python 2.5 with utf-8 files)
    introduced the _indent method from upstream. In Python 3, this converts
    a (unicode) string to bytes, which don't support regex, thus crashing
    when there's a doctest failure (that's the only time this function is
    called). Wrap the encode code with a version check and enter it only
    under Python 2.
  5. @vperic

    ntheory/factor_: Don't use reduce in doctest

    vperic committed
    2to3 crashes when ran on a doctest containing reduce(). This is
    an upstream problem (issue 12611). Replace the only use of this in our
    doctests with a for cycle. This fixes issue #2605.
  6. @vperic

    Fix 2to3 TokenError parsing errors

    vperic committed
    2to3 reports some errors of the type "TokenError: ('EOF in multi-line
    statement', ...)" and can't parse those docstrings. Fix these by
    correctly using multi-line strings and statements in doctests.
  7. @vperic

    physics/wigner: Explicitly cast range() arguments to int

    vperic committed
    In Python 2, a SymPy Float could be used as an argument of range(). This
    is no longer possible in Python 3. There are two occurances of this in
    wigner.py. In the first, a function which expected an integer (per the
    doctest) got a Float and then called range() on it; in the second,
    range() was called directly on Floats. Fix by explicitly casting to int.
  8. @vperic

    ntheory/factor_: Fix namespace leak from list comprehension

    vperic committed
    In Python 3, variables are no longer in the namespace after being used
    in a list comprehension. Fix one doctest to directly use a number
    instead of a variable.
  9. @vperic

    doc/src/*.txt: Use init_printing() instead of setting the displayhook

    vperic committed
    Some .txt doctests were setting the sys.displayhook to pprint which
    produced subtle errors in Python 3 (printing an extra "None" in some
    cases). Instead, use the init_printing() function from
    interactive.printing. It is called by default with use_unicode=false,
    to match the expected output in the files.
    
    These errors only appeared in tutorial.txt and matrices.txt, but use
    init_printing() in four other files which were also setting the
    displayhook.
    
    Also skip a doctest in the tutorial demonstrating the old-style
    division which no longer exists in Python 3 and modify two doctests in
    matrices.txt not to expect a result of None (which wasn't strictly
    correct).
  10. @vperic

    printing/printer: Raise AttributeError in a property

    vperic committed
    The hasattr() function is defined to call getattr() and see if an
    exception is raised (in which case the attribute doesn't exist). This
    has been changed in Python 3.2 to catch only AttributeErrors and not
    all kinds of exceptions. See also issue #2608.
    
    A property in printer.py checked for a key and so could raise a
    KeyError, which now wouldn't be caught by hasattr(). Refactor to raise
    an AttributeError if the key doesn't exist. This bug was caught by a
    test in utilities/tests/test_pickling.
  11. @vperic

    matrices.py: Raise AttributeError in a property

    vperic committed
    The hasattr() function is defined to call getattr() and see if an
    exception is raised (in which case the attribute doesn't exist). This
    has been changed in Python 3.2 to catch only AttributeErrors and not all
    kinds of exceptions. See also issue #2608 and the previous commit.
    
    The property D ("Dirac conjugation") could raise a ShapeError, which now
    wouldn't be caught by hasattr(). Wrap the matrix multiplication with a
    try/except statement that catches ShapeError and returns AttributeError
    instead. Also change a relevant test to expect an AttributeError now.
  12. @vperic

    Add bin/use2to3 script

    vperic committed
    SymPy needs a 2to3 run to be usable in Python 3. Unfortunately, running
    2to3 on mpmath (which is already compatible) produces garbage code. To
    avoid this, we first copy all files (except those from mpmath) to a
    "sympy-py3k" directory, run 2to3 on them and then copy over the files
    from mpmath. One can then use SymPy normally from the sympy-py3k
    directory under Python 3. As running 2to3 is a slow process, we try to
    note which files have been updated since the last run and only process
    those. Also add "sympy-py3k" to .gitignore.
    
    Finally, this is a first draft of the script. As such, there are several
    TODOs which note areas of possible further improvement.
  13. @vperic

    combinatorics/permutations: Use floor division

    vperic committed
    Some code was using the ambiguous division operator on integers. This
    produces floats by default in Python 3 (1.0 as opposed to 1) which
    cannot be used as list indices. Fix by using floor division (//) where
    appropriate. A doctest was also failing, it expected an implicit cast to
    int (1.0 to 1 automatically).
    
    Also remove some redundant casts to int, factorial() returns an int
    always.
  14. @vperic

    utilities/runtest: Make findout_terminal_width Python 3 compatible

    vperic committed
    Commit 76d0046 added a function, findout_terminal_width. This function
    relies on regexing the stdout, which is of type str in Python 2. In
    Python 3 it is a 'bytes' object and it's not possible to regex it, which
    then crashes the doctest runner. Fix by checking if we are under Python
    3 and decoding from "utf-8" if that is the case.
  15. @vperic

    quantum/state: Use floor division in doctest

    vperic committed
    A doctest was using the ambiguous division operator (/). Because the
    expected result was floor division, use the unambiguous floor division
    operator (//). This removes errors under Python 3 and fixes issue #2610.
  16. @vperic

    quantum/__init__: Change imports syntax for Python 3 compatibility

    vperic committed
    The __init__.py file for quantum uses some importing magic so that both
    "from quantum import *" and "from quantum import State" to work. This
    fails under Python 3 because some underlying rules seem to have changed.
    The likely cause is that the module names shadow function names (or
    vice-versa). Fix by directly import __all__ from the affected modules. A
    better fix might be to change the module names, but this is a
    backward-incompatible change and this workaround is equivalent
    functionally.
  17. @smichr @vperic

    doc/src/tutorial: Misc improvements to the documentation

    smichr committed with vperic
    Improve the explanation of the difference between SymPy numbers and
    Python numbers and other small changes.
View
1 .gitignore
@@ -8,6 +8,7 @@ MANIFEST
my/
dist/
build/
+sympy-py3k/
tox.ini
.tox/
View
130 bin/use2to3
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+
+"""
+This script converts SymPy code to a Python 3-compatible version.
+
+The script copies all files except the ones related to mpmath to a sympy-py3k
+directory, runs 2to3 on them and then copies the vanilla mpmath files over. We
+need this because running 2to3 on mpmath (which is already Python 3 compatible)
+produces errors. You can then use SymPy normally from the sympy-py3k directory
+(installing it or importing it directly).
+
+Because copying and running 2to3 can take a lot of time, we try to do it only on
+files that have been modified since the last run.
+
+Note that the 2to3 shipped with Python 2.6 crashes when converting doctests. It
+is recommended to use the Python 3.2 version (or newer) as it is much faster.
+
+TODO: Add --destination argument (others?)
+ --destination # copy over the source to a user-specified destination
+"""
+import os
+import fnmatch
+import shutil
+
+destination = "sympy-py3k" # directory to copy to
+
+# TODO: build this from .gitignore
+skip_dirs = (
+ '.*', # skip hidden dirs; .git and .tox in particular can be quite big
+ 'mpmath', # everything related to mpmath, both in doc/ and sympy/
+ '_build', # files built by Sphinx
+ '__pycache__',
+ 'covhtml', # files produced by bin/test_coverage
+ 'my', # the user can have stuff here we don't want to copy
+ destination # this prevents infinite recursion if the dir already exists
+ )
+
+skip_files = (
+ '*.pyc',
+ '.*',
+ 'ast_parser_python25.py', # this files produces doctest errors under py3k
+ # as we need it only for 2.5, just skip copying it
+ )
+
+modified_files = []
+modified_txt_files = []
+
+# we need to run 2to3 on .txt files; however, not all .txt files are doctests,
+# so we need a list of files we care about
+relevant_txt_files = []
+
+# generate the relevant txt files
+# most of them should be in this directory:
+for root, dirs, files in os.walk('./doc/src/modules'):
+ # NOTE: this will consider mpmath-related files relevant, but it doesn't matter
+ for filename in fnmatch.filter(files, '*.txt'):
+ relevant_txt_files.append(os.path.join(root,filename))
+
+# some files aren't in /doc/src/modules, add them explicitly
+relevant_txt_files.append('./doc/src/tutorial.txt')
+relevant_txt_files.append('./doc/src/gotchas.txt')
+relevant_txt_files.append('./doc/src/guide.txt')
+relevant_txt_files.append('./doc/src/python-comparisons.txt')
+
+# walk the tree and copy over files as necessary
+for root, dirs, files in os.walk('.'):
+ for pattern in skip_dirs:
+ for directory in fnmatch.filter(dirs, pattern):
+ dirs.remove(directory)
+ for pattern in skip_files:
+ for filename in fnmatch.filter(files, pattern):
+ files.remove(filename)
+ for directory in dirs:
+ dstdir = os.path.join(destination, root, directory)
+ if not os.path.exists(dstdir):
+ os.makedirs(dstdir)
+ for filename in files:
+ src = os.path.join(root, filename)
+ dst = os.path.join(destination, root, filename)
+ if os.path.isfile(dst):
+ if os.path.getmtime(src) - os.path.getmtime(dst) < 1:
+ # the file hasn't been modified since the last run, so skip it
+ # we check for one second of difference because Python can be
+ # imprecise (when copying) with smaller time periods
+ continue
+ shutil.copy2(src, dst)
+ # add to the list of files to pass to 2to3 if needed
+ if filename.endswith(".py"):
+ modified_files.append(dst)
+ elif filename.endswith(".txt"):
+ # we need to check the exact path here, not just the filename
+ # as there are eg. multiple index.txt files and not all are relevant
+ if src in relevant_txt_files:
+ modified_txt_files.append(dst)
+
+
+# arguments to call 2to3 with
+args_2to3 = [
+ "-w", # writes back the changes
+ "-n", # doesn't write a backup file
+ "--no-diffs", # don't show the diffs for individual files
+]
+
+args_2to3_doctests = args_2to3 + ["-d"] # convert doctests too
+
+# extend the argument list with the list of files that need it
+args_2to3.extend(modified_files)
+args_2to3_doctests.extend(modified_files)
+args_2to3_doctests.extend(modified_txt_files)
+
+# call 2to3, once for regular files and once for doctests
+from lib2to3.main import main as main2to3
+main2to3("lib2to3.fixes", args_2to3)
+main2to3("lib2to3.fixes", args_2to3_doctests)
+
+# once we are finished with everything, we should finally copy over the files
+# provided by mpmath; these should all be in the following two directories
+
+# to skip checking if something has been updated, just copy everything always
+# the main bottleneck is running 2to3, not copying files
+# TODO: only copy updated files; this would need a heavy modification to the
+# above code, or copy-pasting the relevant part over
+try:
+ shutil.rmtree(os.path.join(destination, "./sympy/mpmath"))
+ shutil.rmtree(os.path.join(destination, "./doc/src/modules/mpmath"))
+except OSError: # directories don't exist
+ pass
+
+shutil.copytree("sympy/mpmath", os.path.join(destination, "./sympy/mpmath"))
+shutil.copytree("doc/src/modules/mpmath", os.path.join(destination, "./doc/src/modules/mpmath"))
View
2 doc/src/gotchas.txt
@@ -279,7 +279,7 @@ to a Python expression. Use the :func:`sympify` function, or just
>>> 6.2 # Python float. Notice the floating point accuracy problems. #doctest: +SKIP
6.2000000000000002
>>> type(6.2)
- <type 'float'>
+ <... 'float'>
>>> S(6.2) # SymPy Float has no such problems because of arbitrary precision.
6.20000000000000
>>> type(S(6.2))
View
20 doc/src/modules/evalf.txt
@@ -46,10 +46,10 @@ numerically, calling ``.evalf()`` or ``N()`` returns the original expression, or
You can also use the standard Python functions ``float()``, ``complex()`` to
convert SymPy expressions to regular Python numbers:
- >>> float(pi)
- 3.14159265359
- >>> complex(pi+E*I)
- (3.14159265359+2.71828182846j)
+ >>> float(pi) #doctest: +SKIP
+ 3.1415926535...
+ >>> complex(pi+E*I) #doctest: +SKIP
+ (3.1415926535...+2.7182818284...j)
If these functions are used, failure to evaluate the expression to an explicit
@@ -125,10 +125,10 @@ where φ is the golden ratio. With ordinary floating-point arithmetic,
subtracting these numbers from each other erroneously results in a complete
cancellation:
- >>> float(GoldenRatio**1000/sqrt(5))
- 4.34665576869e+208
- >>> float(fibonacci(1000))
- 4.34665576869e+208
+ >>> float(GoldenRatio**1000/sqrt(5)) #doctest: +SKIP
+ 4.34665576869...e+208
+ >>> float(fibonacci(1000)) #doctest: +SKIP
+ 4.34665576869...e+208
>>> float(fibonacci(1000)) - float(GoldenRatio**1000/sqrt(5))
0.0
@@ -255,9 +255,7 @@ Oscillatory quadrature requires an integrand containing a factor cos(ax+b) or
sin(ax+b). Note that many other oscillatory integrals can be transformed to
this form with a change of variables:
- >>> from sympy import pprint
- >>> import sys
- >>> sys.displayhook = pprint
+ >>> init_printing(use_unicode=False, wrap_line=False, no_global=True)
>>> intgrl = Integral(sin(1/x), (x, 0, 1)).transform(x, 1/x)
>>> intgrl
oo
View
3 doc/src/modules/integrals.txt
@@ -15,8 +15,7 @@ Examples
SymPy can integrate a vast array of functions. It can integrate polynomial functions::
>>> from sympy import *
- >>> import sys
- >>> sys.displayhook = pprint
+ >>> init_printing(use_unicode=False, wrap_line=False, no_global=True)
>>> x = Symbol('x')
>>> integrate(x**2 + x + 1, x)
3 2
View
7 doc/src/modules/matrices.txt
@@ -9,9 +9,8 @@ Creating Matrices
The linear algebra module is designed to be as simple as possible. First, we
import and declare our first Matrix object:
- >>> from sympy import pprint
- >>> import sys
- >>> sys.displayhook = pprint
+ >>> from sympy.interactive.printing import init_printing
+ >>> init_printing(use_unicode=False, wrap_line=False, no_global=True)
>>> from sympy.matrices import *
>>> Matrix([[1,0], [0,1]])
[1 0]
@@ -217,13 +216,11 @@ All the standard arithmetic operations are supported:
As well as some useful vector operations:
>>> M.row_del(0)
- None
>>> M
[4 5 6]
[ ]
[7 8 9]
>>> M.col_del(1)
- None
>>> M
[4 6]
[ ]
View
5 doc/src/modules/plotting.txt
@@ -150,13 +150,12 @@ gray-scale because we have applied the default color gradient uniformly for
each color component. When defining a color scheme in this way, you might want
to supply a color gradient as well:
- >>> p[1].color = (Fx**2 + Fy**2 + Fz**2)**(0.5),
- ................ (0.1,0.1,0.9), (0.9,0.1,0.1)
+ >>> p[1].color = (Fx**2 + Fy**2 + Fz**2)**(0.5), (0.1,0.1,0.9), (0.9,0.1,0.1)
@smichr SymPy member
smichr added a note

to avoid the long line can this be

>>> p[1].color = (Fx**2 + Fy**2 + Fz**2)**(0.5),
... (0.1,0.1,0.9), (0.9,0.1,0.1)
@vperic
vperic added a note

Actually, the line is just one character over 80 so I think it's fine. I can change it though, it's not a problem.

@smichr SymPy member
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Here's a color gradient with four steps:
>>> gradient = [ 0.0, (0.1,0.1,0.9), 0.3, (0.1,0.9,0.1),
- ................ 0.7, (0.9,0.9,0.1), 1.0, (1.0,0.0,0.0) ]
+ ... 0.7, (0.9,0.9,0.1), 1.0, (1.0,0.0,0.0) ]
>>> p[1].color = (Fx**2 + Fy**2 + Fz**2)**(0.5), gradient
The other way to specify a color scheme is to give a separate function for each
View
4 doc/src/modules/polys/basics.txt
@@ -12,9 +12,7 @@ polynomials within SymPy. All code examples assume::
>>> from sympy import *
>>> x, y, z = symbols('x,y,z')
-
- >>> import sys
- >>> sys.displayhook = pprint
+ >>> init_printing(use_unicode=False, wrap_line=False, no_global=True)
Basic functionality
===================
View
3 doc/src/modules/statistics.txt
@@ -8,8 +8,7 @@ and related tools. Its contents can be imported with the following statement::
>>> from sympy import *
>>> from sympy.statistics import *
- >>> import sys
- >>> sys.displayhook = pprint
+ >>> init_printing(use_unicode=False, wrap_line=False, no_global=True)
Normal distributions
--------------------
View
63 doc/src/tutorial.txt
@@ -125,8 +125,8 @@ Using SymPy as a calculator
Sympy has three built-in numeric types: Float, Rational and Integer.
-The Rational class represents a rational number as a pair of two Integers: the numerator
-and the denominator, so Rational(1,2) represents 1/2, Rational(5,2) 5/2 and so on.
+The Rational class represents a rational number as a pair of two Integers: the numerator and the denominator.
+So Rational(1,2) represents 1/2, Rational(5,2) represents 5/2, and so on.
::
@@ -143,23 +143,33 @@ and the denominator, so Rational(1,2) represents 1/2, Rational(5,2) 5/2 and so o
1/88817841970012523233890533447265625
-proceed with caution while working with python int's since they truncate
-integer division, and that's why::
+Proceed with caution while working with Python int's and floating
+point numbers, especially in division, since you may create a
+Python number, not a SymPy number. A ratio of two Python ints may
+create a float -- the "true division" standard of Python 3
+and the default behavior of ``isympy`` which imports division
+from __future__::
- >>> 1/2
- 0
-
- >>> 1.0/2
- 0.5
+ >>> 1/2 #doctest: +SKIP
+ 0.5
-You can however do::
+But in earlier Python versions where division has not been imported, a
+truncated int will result::
- >>> from __future__ import division
+ >>> 1/2 #doctest: +SKIP
+ 0
- >>> 1/2 #doctest: +SKIP
- 0.5
+In both cases, however, you are not dealing with a SymPy Number because
+Python created its own number. Most of the time you will probably be
+working with Rational numbers, so make sure to use Rational to get
+the SymPy result. One might find it convenient to equate ``R`` and
+Rational::
-True division is going to be standard in python3k and ``isympy`` does that too.
+ >>> R = Rational
+ >>> R(1, 2)
+ 1/2
+ >>> R(1)/2 # R(1) is a sympy Integer and Integer/int gives a Rational
+ 1/2
We also have some special constants, like e and pi, that are treated as symbols
(1+pi won't evaluate to something numeric, rather it will remain as 1+pi), and
@@ -176,7 +186,7 @@ have arbitrary precision::
as you see, evalf evaluates the expression to a floating-point number
-There is also a class representing mathematical infinity, called ``oo``::
+The symbol ``oo`` is used for a class defining mathematical infinity::
>>> oo > 99999
True
@@ -193,7 +203,9 @@ symbolic variables explicitly::
>>> x = Symbol('x')
>>> y = Symbol('y')
-Then you can play with them::
+On the left is the normal Python variable which has been assigned to the
+SymPy Symbol class. Instances of the Symbol class "play well together"
+and are the building blocks of expresions::
>>> x+y+x-y
2*x
@@ -204,7 +216,7 @@ Then you can play with them::
>>> ((x+y)**2).expand()
x**2 + 2*x*y + y**2
-And substitute them for other symbols or numbers using ``subs(old, new)``::
+They can be substituted with other numbers, symbols or expressions using ``subs(old, new)``::
>>> ((x+y)**2).subs(x, 1)
(y + 1)**2
@@ -212,15 +224,16 @@ And substitute them for other symbols or numbers using ``subs(old, new)``::
>>> ((x+y)**2).subs(x, y)
4*y**2
+ >>> ((x+y)**2).subs(x, 1 - y)
+ 1
+
For the remainder of the tutorial, we assume that we have run::
- >>> import sys
- >>> oldhook = sys.displayhook
- >>> sys.displayhook = pprint
+ >>> init_printing(use_unicode=False, wrap_line=False, no_global=True)
-So that things pretty print. See the :ref:`printing-tutorial` section below. If you have
-a unicode font installed, your output may look a little different (it will look slightly
-nicer).
+This will make things look better when printed. See the :ref:`printing-tutorial`
+section below. If you have a unicode font installed, you can pass
+use_unicode=True for a slightly nicer output.
Algebra
=======
@@ -729,7 +742,6 @@ This is what ``str(expression)`` returns and it looks like this:
This is a nice ascii-art printing produced by a ``pprint`` function:
- >>> sys.displayhook = oldhook
>>> from sympy import Integral, pprint
>>> from sympy.abc import x
>>> pprint(x**2)
@@ -768,8 +780,7 @@ Tip: To make the pretty printing default in the python interpreter, use::
[GCC 4.3.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from sympy import *
- >>> import sys
- >>> sys.displayhook = pprint
+ >>> init_printing(use_unicode=False, wrap_line=False, no_global=True)
>>> var("x")
x
>>> x**3/3
View
10 sympy/combinatorics/permutations.py
@@ -295,7 +295,7 @@ def unrank_nonlex(self, n, r):
while n > 1:
id_perm[n-1],id_perm[r % n] = id_perm[r % n], id_perm[n-1]
n -= 1
- r = r/n
+ r = r//n
return Permutation(id_perm)
def rank_nonlex(self, inv_perm = None, n = 0):
@@ -345,7 +345,7 @@ def rank(self):
rank = 0
rho = self.array_form[:]
for j in xrange(self.size - 1):
- rank += (rho[j])*int(factorial(self.size - j - 1))
+ rank += (rho[j])*factorial(self.size - j - 1)
for i in xrange(j + 1, self.size):
if rho[i] > rho[j]:
rho[i] = rho[i] - 1
@@ -774,7 +774,7 @@ def get_precedence_distance(self, other):
continue
if self_prec_mat[i, j] * other_prec_mat[i, j] == 1:
n_prec += 1
- d = self.size * (self.size - 1)/2 - n_prec
+ d = self.size * (self.size - 1)//2 - n_prec
return d
def get_adjacency_matrix(self):
@@ -966,8 +966,8 @@ def unrank_lex(self, size, rank):
perm_array = [0] * size
perm_array[size - 1] = 1
for i in xrange(size - 1):
- d = (rank % int(factorial(i + 1))) / int(factorial(i))
- rank = rank - d*int(factorial(i))
+ d = (rank % factorial(i + 1)) // factorial(i)
+ rank = rank - d*factorial(i)
perm_array[size - i - 1] = d + 1
for j in xrange(size - i, size):
if perm_array[j] > d:
View
9 sympy/matrices/matrices.py
@@ -221,8 +221,13 @@ def H(self):
def D(self):
"""Dirac conjugation."""
from sympy.physics.matrices import mgamma
- out = self.H * mgamma(0)
- return out
+ try:
+ out = self.H * mgamma(0)
+ return out
+ # In Python 3.2, properties can only return an AttributeError, so we
+ # have to catch the ShapeError; see also the commit making this change
+ except ShapeError:
+ raise AttributeError("Dirac conjugation not possible.")
def __getitem__(self,key):
"""
View
2 sympy/matrices/tests/test_matrices.py
@@ -973,7 +973,7 @@ def test_conjugate():
[5,0]])
def test_conj_dirac():
- raises(ShapeError, "eye(3).D")
+ raises(AttributeError, "eye(3).D")
M = Matrix([ [1,I,I,I],
[0,1,I,I],
View
7 sympy/ntheory/factor_.py
@@ -450,14 +450,17 @@ def pollard_pm1(n, B=10, a=2, retries=0, seed=1234):
>>> from sympy.core.numbers import ilcm, igcd
>>> from sympy import factorint, Pow
- >>> M = reduce(ilcm, range(2, 256))
+ >>> M = 1
+ >>> for i in range(2, 256):
+ ... M = ilcm(M, i)
+ ...
>>> set([igcd(pow(a, M, n) - 1, n) for a in range(2, 256) if
... igcd(pow(a, M, n) - 1, n) != n])
set([1009])
But does aM % d for every divisor of n give 1?
- >>> aM = pow(a, M, n)
+ >>> aM = pow(255, M, n)
>>> [(d, aM%Pow(*d.args)) for d in factorint(n, visual=True).args]
[(257**1, 1), (1009**1, 1)]
View
8 sympy/physics/quantum/__init__.py
@@ -15,9 +15,9 @@
from anticommutator import *
__all__.extend(anticommutator.__all__)
-import qapply as qapmod
+from qapply import __all__ as qap_all
from qapply import *
-__all__.extend(qapmod.__all__)
+__all__.extend(qap_all)
import commutator
from commutator import *
@@ -43,9 +43,9 @@
from operator import *
__all__.extend(operator.__all__)
-import represent as repmod
+from represent import __all__ as rep_all
from represent import *
-__all__.extend(repmod.__all__)
+__all__.extend(rep_all)
import state
from state import *
View
12 sympy/physics/quantum/operator.py
@@ -414,8 +414,8 @@ def variables(self):
>>> d.variables
(x,)
>>> y = Symbol('y')
- >>> d = DifferentialOperator(Derivative(f(x, y), x) + \
- Derivative(f(x, y), y), f(x, y))
+ >>> d = DifferentialOperator(Derivative(f(x, y), x) +
+ ... Derivative(f(x, y), y), f(x, y))
>>> d.variables
(x, y)
"""
@@ -438,8 +438,8 @@ def function(self):
>>> d.function
f(x)
>>> y = Symbol('y')
- >>> d = DifferentialOperator(Derivative(f(x, y), x) + \
- Derivative(f(x, y), y), f(x, y))
+ >>> d = DifferentialOperator(Derivative(f(x, y), x) +
+ ... Derivative(f(x, y), y), f(x, y))
>>> d.function
f(x, y)
"""
@@ -463,8 +463,8 @@ def expr(self):
>>> d.expr
Derivative(f(x), x)
>>> y = Symbol('y')
- >>> d = DifferentialOperator(Derivative(f(x, y), x) + \
- Derivative(f(x, y), y), f(x, y))
+ >>> d = DifferentialOperator(Derivative(f(x, y), x) +
+ ... Derivative(f(x, y), y), f(x, y))
>>> d.expr
Derivative(f(x, y), x) + Derivative(f(x, y), y)
"""
View
2 sympy/physics/quantum/state.py
@@ -595,7 +595,7 @@ class Wavefunction(Function):
>>> x = Symbol('x')
>>> n = 1
>>> L = 1
- >>> g = Piecewise((0, x < 0), (0, x > L), (sqrt(2/L)*sin(n*pi*x/L), True))
+ >>> g = Piecewise((0, x < 0), (0, x > L), (sqrt(2//L)*sin(n*pi*x/L), True))
>>> f = Wavefunction(g, x)
>>> f.norm
1
View
4 sympy/physics/wigner.py
@@ -180,7 +180,7 @@ def wigner_3j(j_1, j_2, j_3, m_1, m_2, m_3, prec=None):
maxfact = max(j_1 + j_2 + j_3 + 1, j_1 + abs(m_1), j_2 + abs(m_2), \
j_3 + abs(m_3))
- _calc_factlist(maxfact)
+ _calc_factlist(int(maxfact))
argsqrt = Integer(_Factlist[int(j_1 + j_2 - j_3)] * \
_Factlist[int(j_1 - j_2 + j_3)] * \
@@ -200,7 +200,7 @@ def wigner_3j(j_1, j_2, j_3, m_1, m_2, m_3, prec=None):
imin = max(-j_3 + j_1 + m_2, -j_3 + j_2 - m_1, 0)
imax = min(j_2 + m_2, j_1 - m_1, j_1 + j_2 - j_3)
sumres = 0
- for ii in range(imin, imax + 1):
+ for ii in range(int(imin), int(imax) + 1):
den = _Factlist[ii] * \
_Factlist[int(ii + j_3 - j_1 - m_2)] * \
_Factlist[int(j_2 + m_2 - ii)] * \
View
5 sympy/printing/printer.py
@@ -217,7 +217,10 @@ def set_global_settings(cls, **settings):
@property
def order(self):
- return self._settings['order']
+ if 'order' in self._settings:
+ return self._settings['order']
+ else:
+ raise AttributeError("No order defined.")
def doprint(self, expr):
"""Returns printer's representation for expr (as a string)"""
View
3 sympy/utilities/iterables.py
@@ -1020,8 +1020,7 @@ def generate_involutions(n):
http://mathworld.wolfram.com/PermutationInvolution.html
Examples:
- >>> from sympy.utilities.iterables import \
- generate_involutions
+ >>> from sympy.utilities.iterables import generate_involutions
>>> generate_involutions(3)
[(0, 1, 2), (0, 2, 1), (1, 0, 2), (2, 1, 0)]
>>> len(generate_involutions(4))
View
14 sympy/utilities/runtests.py
@@ -40,8 +40,10 @@ def _indent(s, indent=4):
If the string `s` is Unicode, it is encoded using the stdout
encoding and the `backslashreplace` error handler.
"""
- if isinstance(s, unicode):
- s = s.encode(pdoctest._encoding, 'backslashreplace')
+ # After a 2to3 run the below code is bogus, so wrap it with a version check
+ if sys.version_info[0] < 3:
+ if isinstance(s, unicode):
+ s = s.encode(pdoctest._encoding, 'backslashreplace')
# This regexp matches the start of non-blank lines:
return re.sub('(?m)^(?!$)', indent*' ', s)
@@ -443,7 +445,11 @@ class doctest.Tester, then merges the results into (or creates)
"relative paths.")
# Relativize the path
- text, filename = pdoctest._load_testfile(filename, package, module_relative)
+ if sys.version_info[0] < 3:
+ text, filename = pdoctest._load_testfile(filename, package, module_relative)
+ else:
+ encoding = None
+ text, filename = pdoctest._load_testfile(filename, package, module_relative, encoding)
# If no name was given, then use the file's name.
if name is None:
@@ -1023,6 +1029,8 @@ def findout_terminal_width():
try:
process = subprocess.Popen(['stty', '-a'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout = process.stdout.read()
+ if sys.version_info[0] > 2:
+ stdout = stdout.decode("utf-8")
except (OSError, IOError):
pass
else:
Something went wrong with that request. Please try again.