Feature/dict generation#20
Conversation
|
✅ Code coverage on 🎉 No files have reduced coverage 🎉
|
| File | Details |
|---|---|
src/assertical/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/generator.py |
✅ 100% → 100% |
src/assertical/asserts/pandas.py |
✅ 88% → 88% |
src/assertical/asserts/time.py |
✅ 95% → 95% |
src/assertical/asserts/type.py |
✅ 92% → 92% |
src/assertical/fake/__init__.py |
✅ 100% → 100% |
src/assertical/fake/asyncio.py |
✅ 100% → 100% |
src/assertical/fake/generator.py |
✅ 92% → 92% |
src/assertical/fake/http.py |
✅ 71% → 71% |
src/assertical/fixtures/__init__.py |
✅ 100% → 100% |
src/assertical/fixtures/environment.py |
✅ 100% → 100% |
src/assertical/fixtures/fastapi.py |
✅ 97% → 97% |
src/assertical/fixtures/generator.py |
✅ 100% → 100% |
src/assertical/snapshot.py |
✅ 100% → 100% |
TOTAL |
✅ 90% → 90% |
|
✅ Code coverage on 🎉 No files have reduced coverage 🎉
|
| File | Details |
|---|---|
src/assertical/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/generator.py |
✅ 100% → 100% |
src/assertical/asserts/pandas.py |
✅ 88% → 88% |
src/assertical/asserts/time.py |
✅ 95% → 95% |
src/assertical/asserts/type.py |
✅ 92% → 92% |
src/assertical/fake/__init__.py |
✅ 100% → 100% |
src/assertical/fake/asyncio.py |
✅ 100% → 100% |
src/assertical/fake/generator.py |
✅ 92% → 92% |
src/assertical/fake/http.py |
✅ 71% → 71% |
src/assertical/fixtures/__init__.py |
✅ 100% → 100% |
src/assertical/fixtures/environment.py |
✅ 100% → 100% |
src/assertical/fixtures/fastapi.py |
✅ 97% → 97% |
src/assertical/fixtures/generator.py |
✅ 100% → 100% |
src/assertical/snapshot.py |
✅ 100% → 100% |
TOTAL |
✅ 90% → 90% |
|
✅ Code coverage on 🎉 No files have reduced coverage 🎉
|
| File | Details |
|---|---|
src/assertical/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/generator.py |
✅ 100% → 100% |
src/assertical/asserts/pandas.py |
✅ 88% → 88% |
src/assertical/asserts/time.py |
✅ 95% → 95% |
src/assertical/asserts/type.py |
✅ 92% → 92% |
src/assertical/fake/__init__.py |
✅ 100% → 100% |
src/assertical/fake/asyncio.py |
✅ 100% → 100% |
src/assertical/fake/generator.py |
✅ 92% → 92% |
src/assertical/fake/http.py |
✅ 71% → 71% |
src/assertical/fixtures/__init__.py |
✅ 100% → 100% |
src/assertical/fixtures/environment.py |
✅ 100% → 100% |
src/assertical/fixtures/fastapi.py |
✅ 97% → 97% |
src/assertical/fixtures/generator.py |
✅ 100% → 100% |
src/assertical/snapshot.py |
✅ 100% → 100% |
TOTAL |
✅ 90% → 90% |
|
A fix has also been added to remove the arbitrary increase of 1000 for seeds when handling recursion generation of classes. The code now correctly keeps track of how many values are generated (and by extension what the seed increment should be). |
|
Forgot to mention - overall I'm happy with the approach |
|
✅ Code coverage on 🎉 No files have reduced coverage 🎉
|
| File | Details |
|---|---|
src/assertical/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/generator.py |
✅ 100% → 100% |
src/assertical/asserts/pandas.py |
✅ 88% → 88% |
src/assertical/asserts/time.py |
✅ 95% → 95% |
src/assertical/asserts/type.py |
✅ 92% → 92% |
src/assertical/fake/__init__.py |
✅ 100% → 100% |
src/assertical/fake/asyncio.py |
✅ 100% → 100% |
src/assertical/fake/generator.py |
✅ 92% → 93% |
src/assertical/fake/http.py |
✅ 71% → 71% |
src/assertical/fixtures/__init__.py |
✅ 100% → 100% |
src/assertical/fixtures/environment.py |
✅ 100% → 100% |
src/assertical/fixtures/fastapi.py |
✅ 97% → 97% |
src/assertical/fixtures/generator.py |
✅ 100% → 100% |
src/assertical/snapshot.py |
✅ 100% → 100% |
TOTAL |
✅ 90% → 90% |
|
✅ Code coverage on 🎉 No files have reduced coverage 🎉
|
| File | Details |
|---|---|
src/assertical/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/generator.py |
✅ 100% → 100% |
src/assertical/asserts/pandas.py |
✅ 88% → 88% |
src/assertical/asserts/time.py |
✅ 95% → 95% |
src/assertical/asserts/type.py |
✅ 92% → 92% |
src/assertical/fake/__init__.py |
✅ 100% → 100% |
src/assertical/fake/asyncio.py |
✅ 100% → 100% |
src/assertical/fake/generator.py |
✅ 92% → 93% |
src/assertical/fake/http.py |
✅ 71% → 71% |
src/assertical/fixtures/__init__.py |
✅ 100% → 100% |
src/assertical/fixtures/environment.py |
✅ 100% → 100% |
src/assertical/fixtures/fastapi.py |
✅ 97% → 97% |
src/assertical/fixtures/generator.py |
✅ 100% → 100% |
src/assertical/snapshot.py |
✅ 100% → 100% |
TOTAL |
✅ 90% → 90% |
|
✅ Code coverage on 🎉 No files have reduced coverage 🎉
|
| File | Details |
|---|---|
src/assertical/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/generator.py |
✅ 100% → 100% |
src/assertical/asserts/pandas.py |
✅ 88% → 88% |
src/assertical/asserts/time.py |
✅ 95% → 95% |
src/assertical/asserts/type.py |
✅ 92% → 92% |
src/assertical/fake/__init__.py |
✅ 100% → 100% |
src/assertical/fake/asyncio.py |
✅ 100% → 100% |
src/assertical/fake/generator.py |
✅ 92% → 93% |
src/assertical/fake/http.py |
✅ 71% → 71% |
src/assertical/fixtures/__init__.py |
✅ 100% → 100% |
src/assertical/fixtures/environment.py |
✅ 100% → 100% |
src/assertical/fixtures/fastapi.py |
✅ 97% → 97% |
src/assertical/fixtures/generator.py |
✅ 100% → 100% |
src/assertical/snapshot.py |
✅ 100% → 100% |
TOTAL |
✅ 90% → 90% |
|
✅ Code coverage on 🎉 No files have reduced coverage 🎉
|
| File | Details |
|---|---|
src/assertical/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/generator.py |
✅ 100% → 100% |
src/assertical/asserts/pandas.py |
✅ 88% → 88% |
src/assertical/asserts/time.py |
✅ 95% → 95% |
src/assertical/asserts/type.py |
✅ 92% → 92% |
src/assertical/fake/__init__.py |
✅ 100% → 100% |
src/assertical/fake/asyncio.py |
✅ 100% → 100% |
src/assertical/fake/generator.py |
✅ 92% → 93% |
src/assertical/fake/http.py |
✅ 71% → 71% |
src/assertical/fixtures/__init__.py |
✅ 100% → 100% |
src/assertical/fixtures/environment.py |
✅ 100% → 100% |
src/assertical/fixtures/fastapi.py |
✅ 97% → 97% |
src/assertical/fixtures/generator.py |
✅ 100% → 100% |
src/assertical/snapshot.py |
✅ 100% → 100% |
TOTAL |
✅ 90% → 90% |
|
✅ Code coverage on 🎉 No files have reduced coverage 🎉
|
| File | Details |
|---|---|
src/assertical/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/__init__.py |
✅ 100% → 100% |
src/assertical/asserts/generator.py |
✅ 100% → 100% |
src/assertical/asserts/pandas.py |
✅ 88% → 88% |
src/assertical/asserts/time.py |
✅ 95% → 95% |
src/assertical/asserts/type.py |
✅ 92% → 92% |
src/assertical/fake/__init__.py |
✅ 100% → 100% |
src/assertical/fake/asyncio.py |
✅ 100% → 100% |
src/assertical/fake/generator.py |
✅ 92% → 94% |
src/assertical/fake/http.py |
✅ 71% → 71% |
src/assertical/fixtures/__init__.py |
✅ 100% → 100% |
src/assertical/fixtures/environment.py |
✅ 100% → 100% |
src/assertical/fixtures/fastapi.py |
✅ 97% → 97% |
src/assertical/fixtures/generator.py |
✅ 100% → 100% |
src/assertical/snapshot.py |
✅ 100% → 100% |
TOTAL |
✅ 90% → 91% |
Prior to this PR, assertical could generate only classes instances (including special treatment of dataclasses). Members could be primitive (ints, floats etc), classes (including dataclasses), lists or sets.
This PR allows generation of classes with dictionary members. Not only that, but it allows arbitrary nesting of all collection types (dict, list, etc), for example,
Implementation Details
This PR really contains two separate changes (i) to support generation of dict members and (ii) to support the generation of arbitrary nesting of all collection types. The sections below discuss details of each implementation separately to make reviewing the code hopefully a little easier.
Generation of dict members
Adds necessary dicts enum values to
CollectionType.Unlike lists or set, dicts have two types (one for the keys and one for the values). To support this
PropertyGenerationDetailshas been extended to store type information for the second type (namelysecond_type_to_generate,second_type_is_primitiveattributes). These get set for dictionaries in theenumerate_class_propertiesfunction.One complication is this second value (dict value) needs to be generated in a similar way to first (dict key), except using
second_type_to_generateandsecond_type_is_primitiveinstead oftype_to_generateandtype_is_primitive. To this end the value generation logic is pulled out into an inner function calledgenerate_value(). This inner function is a closure capturing the values ofoptional_is_none,generate_relationshipsand_visited_type_stack. This inner function can be found within thegenerate_class_instancefunction.Generation of nested collections types
The change discussed above is sufficient to generate dictionaries of primitive values e.g. dict[str, int], dict[int, float] and also generate dictionaries with class values e.g. dict[str, MyDataclass], dict[int, MyClass] (assuming generate_relationship is True).
I wanted to extend the generation to also be able to handle arbitrary nesting of collection types e.g. dict[int, list[int]], list[dict[str, MyClass]].
This is tricky but possible if we treat the collections as-if they were classes and use the machinery already in-place to perform class instance generation.
_PlaceholderCollectionBaseas "base-class" for all the collections type.public_member_checker) and for getting types hints (type_hints_fetcher). There is now also a TYPE_HINT_FETCHER global for storing the per-type 'type hints fetcher' function._PlaceholderCollectionBaseis registered base type. To get class-like behaviour from a collection, we override the instance generator, the member fetcher, the type-hint fetcher and the is-public-member checker.get_generatable_class_base, allows it to recognise collection types and return_PlaceholderCollectionBasewhen appropriate.A consequence of this change, is that it's now possible to generate lists, dicts or sets directly with a call to
generate_class_instance, since collections are themselves treated as pseudo-classes, for example,