diff options
-rw-r--r-- | .travis.yml | 1 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | README.rst | 6 | ||||
-rw-r--r-- | dev_requirements.txt | 3 | ||||
-rw-r--r-- | docs/conf.py | 2 | ||||
-rw-r--r-- | docs/orms.rst | 2 | ||||
-rw-r--r-- | docs/reference.rst | 17 | ||||
-rw-r--r-- | factory/compat.py | 8 | ||||
-rw-r--r-- | factory/fuzzy.py | 2 | ||||
-rw-r--r-- | tests/test_fuzzy.py | 13 | ||||
-rw-r--r-- | tests/test_using.py | 30 |
11 files changed, 74 insertions, 14 deletions
diff --git a/.travis.yml b/.travis.yml index e1600bd..ff805b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: python python: - "2.7" - "3.4" + - "3.5" - "pypy" script: @@ -7,13 +7,13 @@ EXAMPLES_DIR=examples COVERAGE = python $(shell which coverage) # Dependencies -DJANGO ?= 1.8 +DJANGO ?= 1.9 NEXT_DJANGO = $(shell python -c "v='$(DJANGO)'; parts=v.split('.'); parts[-1]=str(int(parts[-1])+1); print('.'.join(parts))") ALCHEMY ?= 1.0 NEXT_ALCHEMY = $(shell python -c "v='$(ALCHEMY)'; parts=v.split('.'); parts[-1]=str(int(parts[-1])+1); print('.'.join(parts))") -MONGOENGINE ?= 0.9 +MONGOENGINE ?= 0.10 NEXT_MONGOENGINE = $(shell python -c "v='$(MONGOENGINE)'; parts=v.split('.'); parts[-1]=str(int(parts[-1])+1); print('.'.join(parts))") REQ_FILE = auto_dev_requirements_django$(DJANGO)_alchemy$(ALCHEMY)_mongoengine$(MONGOENGINE).txt @@ -181,7 +181,7 @@ It is also possible to create a bunch of objects in a single call: Realistic, random values """""""""""""""""""""""" -Tests look better with random yet realistic values. +Demos look better with random yet realistic values; and those realistic values can also help discover bugs. For this, factory_boy relies on the excellent `fake-factory <https://pypi.python.org/pypi/fake-factory>`_ library: .. code-block:: python @@ -199,6 +199,10 @@ For this, factory_boy relies on the excellent `fake-factory <https://pypi.python <User: Lucy Murray> +.. note:: Use of fully randomized data in tests is quickly a problem for reproducing broken builds. + To that purpose, factory_boy provides helpers to handle the random seeds it uses. + + Lazy Attributes """"""""""""""" diff --git a/dev_requirements.txt b/dev_requirements.txt index 22261a1..c78aa9d 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -8,3 +8,6 @@ SQLAlchemy mongoengine mock wheel + +Sphinx +sphinx_rtd_theme diff --git a/docs/conf.py b/docs/conf.py index c3512e0..d5b86f4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -114,7 +114,7 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/orms.rst b/docs/orms.rst index bd481bd..d1b30fc 100644 --- a/docs/orms.rst +++ b/docs/orms.rst @@ -126,7 +126,7 @@ Extra fields :param str from_path: Use data from the file located at ``from_path``, and keep its filename :param file from_file: Use the contents of the provided file object; use its filename - if available + if available, unless ``filename`` is also provided. :param bytes data: Use the provided bytes as file contents :param str filename: The filename for the FileField diff --git a/docs/reference.rst b/docs/reference.rst index 6398d9a..b5ccd16 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -1412,6 +1412,23 @@ If a value if passed for the :class:`RelatedFactory` attribute, this disables 1 +.. note:: The target of the :class:`RelatedFactory` is evaluated *after* the initial factory has been instantiated. + This means that calls to :class:`factory.SelfAttribute` cannot go higher than this :class:`RelatedFactory`: + + .. code-block:: python + + class CountryFactory(factory.Factory): + class Meta: + model = Country + + lang = 'fr' + capital_city = factory.RelatedFactory(CityFactory, 'capital_of', + # factory.SelfAttribute('..lang') will crash, since the context of + # ``CountryFactory`` has already been evaluated. + main_lang=factory.SelfAttribute('capital_of.lang'), + ) + + PostGeneration """""""""""""" diff --git a/factory/compat.py b/factory/compat.py index 785d174..737d91a 100644 --- a/factory/compat.py +++ b/factory/compat.py @@ -42,14 +42,6 @@ else: # pragma: no cover from io import BytesIO -if sys.version_info[:2] == (2, 6): # pragma: no cover - def float_to_decimal(fl): - return decimal.Decimal(str(fl)) -else: # pragma: no cover - def float_to_decimal(fl): - return decimal.Decimal(fl) - - try: # pragma: no cover # Python >= 3.2 UTC = datetime.timezone.utc diff --git a/factory/fuzzy.py b/factory/fuzzy.py index 923d8b7..a7e834c 100644 --- a/factory/fuzzy.py +++ b/factory/fuzzy.py @@ -164,7 +164,7 @@ class FuzzyDecimal(BaseFuzzyAttribute): super(FuzzyDecimal, self).__init__(**kwargs) def fuzz(self): - base = compat.float_to_decimal(_random.uniform(self.low, self.high)) + base = decimal.Decimal(str(_random.uniform(self.low, self.high))) return base.quantize(decimal.Decimal(10) ** -self.precision) diff --git a/tests/test_fuzzy.py b/tests/test_fuzzy.py index 3f9c434..4c3873a 100644 --- a/tests/test_fuzzy.py +++ b/tests/test_fuzzy.py @@ -189,6 +189,19 @@ class FuzzyDecimalTestCase(unittest.TestCase): self.assertEqual(decimal.Decimal('8.001').quantize(decimal.Decimal(10) ** -3), res) + @unittest.skipIf(compat.PY2, "decimal.FloatOperation was added in Py3") + def test_no_approximation(self): + """We should not go through floats in our fuzzy calls unless actually needed.""" + fuzz = fuzzy.FuzzyDecimal(0, 10) + + decimal_context = decimal.getcontext() + old_traps = decimal_context.traps[decimal.FloatOperation] + try: + decimal_context.traps[decimal.FloatOperation] = True + fuzz.evaluate(2, None, None) + finally: + decimal_context.traps[decimal.FloatOperation] = old_traps + class FuzzyDateTestCase(unittest.TestCase): @classmethod diff --git a/tests/test_using.py b/tests/test_using.py index c7d2b85..0a893c1 100644 --- a/tests/test_using.py +++ b/tests/test_using.py @@ -1924,6 +1924,36 @@ class PostGenerationTestCase(unittest.TestCase): self.assertEqual(3, related.one) self.assertEqual(4, related.two) + def test_related_factory_selfattribute(self): + class TestRelatedObject(object): + def __init__(self, obj=None, one=None, two=None): + obj.related = self + self.one = one + self.two = two + self.three = obj + + class TestRelatedObjectFactory(factory.Factory): + class Meta: + model = TestRelatedObject + one = 1 + two = factory.LazyAttribute(lambda o: o.one + 1) + + class TestObjectFactory(factory.Factory): + class Meta: + model = TestObject + one = 3 + two = 2 + three = factory.RelatedFactory(TestRelatedObjectFactory, 'obj', + two=factory.SelfAttribute('obj.two'), + ) + + obj = TestObjectFactory.build(two=4) + self.assertEqual(3, obj.one) + self.assertEqual(4, obj.two) + self.assertEqual(1, obj.related.one) + self.assertEqual(4, obj.related.two) + + class RelatedFactoryExtractionTestCase(unittest.TestCase): def setUp(self): |