Skip to content

Commit

Permalink
Enable VSCode Testing to scan tests successfully (#435)
Browse files Browse the repository at this point in the history
Changes:
- Avoid using `current_domain` where domain object is readily accessible
- Rename `test_domains` directory to `domains` in `tests/support`
- Add `tests/support` to ignore path in .vscode/settings file
- Change files to throw OutOfContextError now that there is no import time
access to OutOfContextError
  • Loading branch information
subhashb authored Jun 9, 2024
1 parent c9813bf commit ba96b80
Show file tree
Hide file tree
Showing 79 changed files with 332 additions and 59 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,6 @@ output/*/index.html
.testmondata
*.rdb

# VS Code
.vscode

# Sphinx
docs-sphinx/.doctrees

Expand Down
147 changes: 147 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Protean CLI",
"type": "debugpy",
"request": "launch",
"module": "protean",
"console": "integratedTerminal",
"justMyCode": false,
"args": [
"new",
"-o",
"/Users/subhashb/wspace/proteanhq/test-scaffolding",
"-d",
"author_name=Subhash Bhushan",
"-d",
"[email protected]",
]
},
{
"name": "Python: Debug Tests",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"purpose": ["debug-test"],
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "Python: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "Python: Pytest",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"justMyCode": false,
"args": [
"tests/test_aggregates.py::TestAggregateIdentity::test_that_abstract_aggregates_do_not_have_id_field",
]
},
{
"name": "Python: Sqlite - Specific test case",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"justMyCode": false,
"args": [
"tests/adapters/repository/sqlalchemy_repo/sqlite/test_provider.py::TestProviders::test_provider_raw",
"--sqlite"
]
},
{
"name": "Python: Postgres - Specific test case",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"justMyCode": false,
"args": [
"tests/adapters/model/sqlalchemy_model/postgresql/test_array_datatype.py::test_array_data_type_association",
"--postgresql"
]
},
{
"name": "Python: Redis - Specific test case",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": [
"tests/adapters/broker/redis_broker/tests.py::TestPublishingToRedis::test_event_message_structure",
"--redis"
]
},
{
"name": "Python: MessageDB - Specific test case",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": [
"tests/adapters/event_store/message_db_event_store/tests.py::TestMessageDBEventStore::test_error_on_message_db_initialization",
"--message_db"
]
},
{
"name": "Python: Elasticsearch Generic",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"justMyCode": false,
"args": [
"tests/adapters/repository/test_generic.py::TestConcurrency::test_expected_version_error_on_version_mismatch",
"--db=ELASTICSEARCH"
]
},
{
"name": "Python: Elasticsearch",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"justMyCode": false,
"args": [
"tests/adapters/model/elasticsearch_model/tests.py::TestModelOptions::TestModelSettings::test_settings_override_in_custom_model",
"--elasticsearch"
]
},
{
"name": "Python: All Postgresql",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": ["--postgresql"]
},
{
"name": "Generic Tests - Postgresql",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": [
"tests/adapters/repository/test_generic.py::TestPersistenceViaRepository::test_that_aggregate_can_be_persisted_with_repository",
"-m",
"database",
"--db=POSTGRESQL",
"-sv"
]
},
{
"name": "Generic Tests - Elasticsearch",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": [
"tests/adapters/repository/test_generic.py::TestPersistenceViaRepository::test_that_aggregate_can_be_persisted_with_repository",
"--db=elasticsearch",
"-sv"
]
}
]
}
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"python.testing.pytestArgs": [
"tests",
"--ignore=tests/support"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
3 changes: 0 additions & 3 deletions src/protean/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from .core.value_object import BaseValueObject
from .core.view import BaseView
from .domain import Domain
from .globals import current_domain, current_uow
from .server import Engine
from .utils import get_version
from .utils.mixins import handle
Expand Down Expand Up @@ -47,8 +46,6 @@
"QuerySet",
"UnitOfWork",
"apply",
"current_domain",
"current_uow",
"get_version",
"handle",
"invariant",
Expand Down
27 changes: 12 additions & 15 deletions src/protean/adapters/repository/sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
from protean.exceptions import (
ConfigurationError,
ObjectNotFoundError,
OutOfContextError,
)
from protean.fields import (
Auto,
Expand Down Expand Up @@ -95,22 +94,20 @@ def _get_identity_type():
If `current_domain` is not yet available, it simply means that Protean is still being loaded.
Default to `Identity.STRING`
Raises:
OutOfContextError: If the method is called outside the context of a domain
"""
try:
if current_domain.config["identity_type"] == IdentityType.INTEGER.value:
return sa_types.Integer
elif current_domain.config["identity_type"] == IdentityType.STRING.value:
return sa_types.String
elif current_domain.config["identity_type"] == IdentityType.UUID.value:
return GUID
else:
raise ConfigurationError(
f'Unknown Identity Type {current_domain.config["identity_type"]}'
)
except OutOfContextError:
# This happens only when the module is being imported the first time.
# All further calls will have `current_domain` available.
if current_domain.config["identity_type"] == IdentityType.INTEGER.value:
return sa_types.Integer
elif current_domain.config["identity_type"] == IdentityType.STRING.value:
return sa_types.String
elif current_domain.config["identity_type"] == IdentityType.UUID.value:
return GUID
else:
raise ConfigurationError(
f'Unknown Identity Type {current_domain.config["identity_type"]}'
)


def _default(value):
Expand Down
4 changes: 2 additions & 2 deletions src/protean/core/event_sourced_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
NotSupportedError,
)
from protean.fields import Identifier
from protean.globals import current_domain, current_uow
from protean.globals import current_uow
from protean.utils import DomainObjects, derive_element_class

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -54,7 +54,7 @@ def get(self, identifier: Identifier) -> BaseEventSourcedAggregate:
if current_uow and identifier in current_uow._identity_map:
return current_uow._identity_map[identifier]

aggregate = current_domain.event_store.store.load_aggregate(
aggregate = self._domain.event_store.store.load_aggregate(
self.meta_.part_of, identifier
)

Expand Down
11 changes: 5 additions & 6 deletions src/protean/core/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from protean.container import Element, OptionsMixin
from protean.exceptions import IncorrectUsageError, NotSupportedError
from protean.fields import HasMany, HasOne
from protean.globals import current_domain
from protean.reflection import association_fields, has_association_fields
from protean.utils import (
Database,
Expand Down Expand Up @@ -147,23 +146,23 @@ def _sync_children(self, entity):
# If the item was changed directly AND added via `add`, then
# we give preference to the object in the cache
if item not in entity._temp_cache[field_name]["updated"]:
current_domain.repository_for(field.to_cls)._dao.save(item)
self._domain.repository_for(field.to_cls)._dao.save(item)

for _, item in entity._temp_cache[field_name]["removed"].items():
current_domain.repository_for(field.to_cls)._dao.delete(item)
self._domain.repository_for(field.to_cls)._dao.delete(item)
entity._temp_cache[field_name][
"removed"
] = {} # Empty contents of `removed` cache

for _, item in entity._temp_cache[field_name]["updated"].items():
current_domain.repository_for(field.to_cls)._dao.save(item)
self._domain.repository_for(field.to_cls)._dao.save(item)
entity._temp_cache[field_name][
"updated"
] = {} # Empty contents of `updated` cache

for _, item in entity._temp_cache[field_name]["added"].items():
item.state_.mark_new()
current_domain.repository_for(field.to_cls)._dao.save(item)
self._domain.repository_for(field.to_cls)._dao.save(item)
entity._temp_cache[field_name][
"added"
] = {} # Empty contents of `added` cache
Expand All @@ -173,7 +172,7 @@ def _sync_children(self, entity):
# These are ones whose attributes have been changed directly
# instead of being routed via `add`/`remove`
item = getattr(entity, field_name)
to_cls_repo = current_domain.repository_for(field.to_cls)
to_cls_repo = self._domain.repository_for(field.to_cls)
if item is not None and item.state_.is_changed:
to_cls_repo._dao.save(item)
# Or a new instance has been assigned
Expand Down
5 changes: 2 additions & 3 deletions src/protean/domain/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
from protean.domain.registry import _DomainRegistry
from protean.exceptions import ConfigurationError, IncorrectUsageError
from protean.fields import HasMany, HasOne, Reference, ValueObject
from protean.globals import current_domain
from protean.reflection import declared_fields, has_fields
from protean.utils import (
CommandProcessing,
Expand Down Expand Up @@ -815,9 +814,9 @@ def publish(self, event: BaseEvent) -> None:

self.brokers.publish(event)

if current_domain.config["event_processing"] == EventProcessing.SYNC.value:
if self.config["event_processing"] == EventProcessing.SYNC.value:
# Consume events right-away
handler_classes = current_domain.handlers_for(event)
handler_classes = self.handlers_for(event)
for handler_cls in handler_classes:
handler_methods = (
handler_cls._handlers[fqn(event.__class__)]
Expand Down
7 changes: 5 additions & 2 deletions src/protean/fields/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def __init__(
messages.update(error_messages or {})
self.error_messages = messages

def _generic_param_values_for_repr(self):
def _generic_param_values_for_repr(self) -> list[str]:
"""Return the generic parameter values for the Field's repr"""
values = []
if self.description:
Expand All @@ -117,7 +117,10 @@ def _generic_param_values_for_repr(self):
if callable(self.default):
values.append(f"default={self.default.__name__}")
else:
values.append(f"default='{self.default}'")
if isinstance(self.default, str):
values.append(f"default='{self.default}'")
else:
values.append(f"default={self.default}")
return values

def __repr__(self):
Expand Down
Loading

0 comments on commit ba96b80

Please sign in to comment.