Circular imports

asked 2021-07-26 16:31:29 +0200

Mathé gravatar image

Hello,

I'm writing two classes which depend on each other. They are very similar to Integer and Rational, so let's just take that example. Rationals are constructed using instances of Integer. But some operations on an Integer return a Rational (such as division).

Currently I have two files: integer.py and rational.py. At the top of rational.py I do

from integer import Integer, Integers

And within a few specific methods of Integer (e.g. _div_()), I do

from rational import Rational

or

from rational import Rationals

This worked fine, but now that I am running the doctests using the SageMath doctesting framework:

./sage -t integer.py

I get errors. I traced these errors back to the following problem. My parents Integers and Rationals are unique: they inherit from UniqueRepresentation. When running the tests, two different instances of Integers are created. I think this is because I first create such an instance, then I perform for example a division, which imports from rational.py, which in turn imports integer.py, thereby redefining Integers. Hence a newly created instance of Integers is another one than the first one. This causes my tests to break.

This problem does not arise when I work in the SageMath shell/console. I read two little paragraphs in the SageMath documentation suggesting that I should do something like the following in the doctests of for example my division function in integer.py:

import __main__
__main__.Integers = Integers

But I got no success with this. Also I do not fully understand what this does.

I tried looking at the actual SageMath library files which implement integers/rational numbers. But there the files seem to me like they import from their C or Cython versions, which I have no experience with and do not use myself.

Does anybody know a clean way to get rid of this problem?

edit retag flag offensive close merge delete

Comments

One way of having no problems with circular imports is to just use import rational and import integer and refer to integer.Integers and rational.Rationals. Reimporting a module should not run its initialization code again. It sounds a little fishy that your UniqueRepresentation class can be enticed to return two instances. I would take a look in the UniqueRepresentation cache (it's a WeakValue dictionary) and see under which keys the two instances are stored, Apparently these keys (the construction parameters!) do not compare equal; otherwise the cacheshould have picked it up.

Messing about with __main__ assignments is surely a rather bad hack that at best works around the problem, but doesn't solve it.

I think you'll be better off asking about this in sage-support or sage-devel

nbruin gravatar imagenbruin ( 2021-07-27 03:31:11 +0200 )edit