diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b002352..aaf65a0c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.11", "3.12"] name: Python ${{ matrix.python-version }} Tests services: diff --git a/setup.py b/setup.py index 0052baec..46aa36b3 100644 --- a/setup.py +++ b/setup.py @@ -27,8 +27,8 @@ def read(*names, **kwargs): elasticsearch_requires = ["elasticsearch~=7.17.9", "elasticsearch-dsl~=7.4.1"] redis_requires = ["redis==3.5.2"] -sqlite_requires = ["sqlalchemy==1.4.47"] -postgresql_requires = ["psycopg2>=2.8.4", "sqlalchemy==1.4.47"] +sqlite_requires = ["sqlalchemy~=1.4.50"] +postgresql_requires = ["psycopg2>=2.9.9", "sqlalchemy~=1.4.50"] celery_requires = ["celery[redis]~=5.2.7"] sendgrid_requires = ["sendgrid>=6.1.3"] flask_requires = ["flask>=1.1.1"] @@ -41,7 +41,7 @@ def read(*names, **kwargs): "cookiecutter>=1.7.0", "copier>=6.1.0", "inflection>=0.5.1", - "python-dateutil>=2.8.1", + "python-dateutil>=2.8.2", "werkzeug>=2.0.0", ] @@ -58,20 +58,20 @@ def read(*names, **kwargs): testing_requires = all_external_requires + [ "autoflake>=2.2.1", - "isort>=5.10.1", - "mock==4.0.2", - "pluggy==0.13.1", - "pytest-asyncio>=0.15.1", - "pytest-cov==2.8.1", - "pytest-flake8>=1.0.7", - "pytest-mock==3.1.0", - "pytest>=5.4.2", + "isort>=5.12.0", + "mock==5.1.0", + "pluggy==1.3.0", + "pytest-asyncio>=0.21.1", + "pytest-cov>=4.1.0", + "pytest-flake8>=1.1.1", + "pytest-mock==3.12.0", + "pytest>=7.4.3", ] docs_requires = [ "livereload>=2.6.3", - "sphinx>=4.1.2", - "sphinx-tabs>=3.2.0", + "sphinx>=7.2.6", + "sphinx-tabs>=3.4.4", ] types_requires = [ @@ -86,13 +86,13 @@ def read(*names, **kwargs): + types_requires + testing_requires + [ - "black==21.11b1", - "check-manifest==0.42", - "coverage==5.1", - "docutils==0.16", + "black>=23.11.0", + "check-manifest>=0.49", + "coverage>=7.3.2", + "docutils>=0.18.0", "pre-commit>=2.16.0", - "tox==3.15.0", - "twine==3.1.1", + "tox>=4.11.3", + "twine>=4.0.2", ] ) @@ -123,9 +123,6 @@ def read(*names, **kwargs): "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3 :: Only", diff --git a/src/protean/adapters/event_store/memory.py b/src/protean/adapters/event_store/memory.py index a41e1cda..be94c795 100644 --- a/src/protean/adapters/event_store/memory.py +++ b/src/protean/adapters/event_store/memory.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime from typing import Any, Dict, List from protean import BaseAggregate, BaseRepository @@ -57,7 +57,7 @@ def write( type=message_type, data=data, metadata=MessageMetadata(**metadata) if metadata else None, - time=datetime.utcnow(), + time=datetime.now(UTC), ) ) diff --git a/src/protean/adapters/repository/elasticsearch.py b/src/protean/adapters/repository/elasticsearch.py index e9f0cbeb..ece73407 100644 --- a/src/protean/adapters/repository/elasticsearch.py +++ b/src/protean/adapters/repository/elasticsearch.py @@ -481,7 +481,7 @@ def _data_reset(self): if provider.conn_info[ "DATABASE" ] == Database.ELASTICSEARCH.value and conn.indices.exists( - model_cls._index._name + index=model_cls._index._name ): conn.delete_by_query( refresh=True, @@ -522,7 +522,7 @@ def _drop_database_artifacts(self): if provider.conn_info[ "DATABASE" ] == Database.ELASTICSEARCH.value and model_cls._index.exists(using=conn): - conn.indices.delete(model_cls._index._name) + conn.indices.delete(index=model_cls._index._name) class DefaultLookup(BaseLookup): diff --git a/src/protean/container.py b/src/protean/container.py index 427a376f..71ebb7e8 100644 --- a/src/protean/container.py +++ b/src/protean/container.py @@ -164,13 +164,6 @@ def __new__(mcs, name, bases, attrs, **kwargs): if attr_name not in fields_dict } - # Propagate `__classcell__` if present to the `type.__new__` call. - # Failing to do so will result in a RuntimeError in Python 3.8. - # https://docs.python.org/3/reference/datamodel.html#creating-the-class-object - classcell = attrs.pop("__classcell__", None) - if classcell is not None: - dup_attrs["__classcell__"] = classcell - # Insert fields in the order in which they were specified # When field names overlap, the last specified field wins dup_attrs.update(fields_dict) diff --git a/src/protean/domain/helpers.py b/src/protean/domain/helpers.py index 52822c85..ae939b41 100644 --- a/src/protean/domain/helpers.py +++ b/src/protean/domain/helpers.py @@ -1,6 +1,6 @@ +import importlib.util import logging import os -import pkgutil import sys logger = logging.getLogger(__name__) @@ -40,7 +40,7 @@ def get_root_path(import_name): return os.path.dirname(os.path.abspath(mod.__file__)) # Next attempt: check the loader. - loader = pkgutil.get_loader(import_name) + loader = importlib.util.find_spec(import_name) # Loader does not exist or we're referring to an unloaded main module # or a main module without path (interactive sessions), go with the diff --git a/src/protean/server/engine.py b/src/protean/server/engine.py index 70601e34..aa01e953 100644 --- a/src/protean/server/engine.py +++ b/src/protean/server/engine.py @@ -10,7 +10,7 @@ from protean.core.event_handler import BaseEventHandler from protean.exceptions import ConfigurationError from protean.globals import g -from protean.utils.importlib import import_from_full_path +from protean.utils import import_from_full_path from protean.utils.mixins import Message from .subscription import Subscription diff --git a/src/protean/utils/__init__.py b/src/protean/utils/__init__.py index 571499a7..be2dffe7 100644 --- a/src/protean/utils/__init__.py +++ b/src/protean/utils/__init__.py @@ -4,14 +4,14 @@ to the maximum extent possible. """ import functools +import importlib import logging +from datetime import UTC, datetime from enum import Enum, auto from typing import Any, Tuple, Union from uuid import uuid4 -import pkg_resources - from protean.exceptions import ConfigurationError, IncorrectUsageError from protean.globals import current_domain @@ -64,8 +64,21 @@ def __eq__(self, other): return isinstance(other, self.expected_type) +def utcnow_func(): + """Return the current time in UTC with timezone information""" + return datetime.now(UTC) + + def get_version(): - return pkg_resources.require("protean")[0].version + return importlib.metadata.version("protean") + + +def import_from_full_path(domain, path): + spec = importlib.util.spec_from_file_location(domain, path) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + + return getattr(mod, domain) def fully_qualified_name(cls): diff --git a/src/protean/utils/importlib.py b/src/protean/utils/importlib.py index 328ad8a3..8b137891 100644 --- a/src/protean/utils/importlib.py +++ b/src/protean/utils/importlib.py @@ -1,40 +1 @@ -""" Module defines utilities for importing modules and packages """ -import importlib.util - -from importlib import import_module - - -def perform_import(val): - """ - If the given setting is a string import notation, - then perform the necessary import or imports. - """ - if val is not None: - if isinstance(val, str): - return import_from_string(val) - elif isinstance(val, (list, tuple)): - return [import_from_string(item) for item in val] - - return val - - -def import_from_string(val, package=None): - """ - Attempt to import a class from a string representation. - """ - try: - module_path, class_name = val.rsplit(".", 1) - module = import_module(module_path, package=package) - return getattr(module, class_name) - except (ImportError, AttributeError) as e: - msg = f"Could not import {val}. {e.__class__.__name__}: {e}" - raise ImportError(msg) - - -def import_from_full_path(domain, path): - spec = importlib.util.spec_from_file_location(domain, path) - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - - return getattr(mod, domain) diff --git a/tests/adapters/model/elasticsearch_model/tests.py b/tests/adapters/model/elasticsearch_model/tests.py index 721d33aa..cd604285 100644 --- a/tests/adapters/model/elasticsearch_model/tests.py +++ b/tests/adapters/model/elasticsearch_model/tests.py @@ -346,7 +346,7 @@ class ReceiverInlineModel: model_cls = test_domain.repository_for(Receiver)._model conn = test_domain.providers["default"].get_connection() if model_cls._index.exists(using=conn): - conn.indices.delete(model_cls._index._name) + conn.indices.delete(index=model_cls._index._name) model_cls.init(using=conn) receiver_dao = test_domain.repository_for(Receiver)._dao @@ -356,4 +356,4 @@ class ReceiverInlineModel: assert receiver is not None assert receiver.name == "John" - conn.indices.delete(model_cls._index._name) + conn.indices.delete(index=model_cls._index._name) diff --git a/tests/adapters/model/sqlalchemy_model/postgresql/test_array_datatype.py b/tests/adapters/model/sqlalchemy_model/postgresql/test_array_datatype.py index 45320566..b8ec3604 100644 --- a/tests/adapters/model/sqlalchemy_model/postgresql/test_array_datatype.py +++ b/tests/adapters/model/sqlalchemy_model/postgresql/test_array_datatype.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime import pytest @@ -110,7 +110,7 @@ def test_array_content_type_validation(test_domain): {"email": "john.doe@gmail.com", "roles": [1, 2]}, {"email": "john.doe@gmail.com", "roles": ["1", 2]}, {"email": "john.doe@gmail.com", "roles": [1.0, 2.0]}, - {"email": "john.doe@gmail.com", "roles": [datetime.utcnow()]}, + {"email": "john.doe@gmail.com", "roles": [datetime.now(UTC)]}, ]: with pytest.raises(ValidationError) as exception: ArrayUser(**kwargs) @@ -127,7 +127,7 @@ def test_array_content_type_validation(test_domain): for kwargs in [ {"email": "john.doe@gmail.com", "roles": ["ADMIN", "USER"]}, {"email": "john.doe@gmail.com", "roles": ["1", "2"]}, - {"email": "john.doe@gmail.com", "roles": [datetime.utcnow()]}, + {"email": "john.doe@gmail.com", "roles": [datetime.now(UTC)]}, ]: with pytest.raises(ValidationError) as exception: IntegerArrayUser(**kwargs) diff --git a/tests/adapters/model/sqlalchemy_model/postgresql/test_json_datatype.py b/tests/adapters/model/sqlalchemy_model/postgresql/test_json_datatype.py index aec35a8d..20541eda 100644 --- a/tests/adapters/model/sqlalchemy_model/postgresql/test_json_datatype.py +++ b/tests/adapters/model/sqlalchemy_model/postgresql/test_json_datatype.py @@ -1,5 +1,3 @@ -from datetime import datetime - import pytest from sqlalchemy import types as sa_types @@ -7,11 +5,12 @@ from protean import BaseAggregate from protean.fields import DateTime, Dict, String from protean.globals import current_domain +from protean.utils import utcnow_func class Event(BaseAggregate): name = String(max_length=255) - created_at = DateTime(default=datetime.utcnow()) + created_at = DateTime(default=utcnow_func) payload = Dict() diff --git a/tests/adapters/model/sqlalchemy_model/postgresql/test_list_datatype.py b/tests/adapters/model/sqlalchemy_model/postgresql/test_list_datatype.py index 1f43348f..0d3d85d9 100644 --- a/tests/adapters/model/sqlalchemy_model/postgresql/test_list_datatype.py +++ b/tests/adapters/model/sqlalchemy_model/postgresql/test_list_datatype.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime import pytest @@ -40,7 +40,7 @@ def test_array_content_type_validation(test_domain): {"email": "john.doe@gmail.com", "roles": [1, 2]}, {"email": "john.doe@gmail.com", "roles": ["1", 2]}, {"email": "john.doe@gmail.com", "roles": [1.0, 2.0]}, - {"email": "john.doe@gmail.com", "roles": [datetime.utcnow()]}, + {"email": "john.doe@gmail.com", "roles": [datetime.now(UTC)]}, ]: with pytest.raises(ValidationError) as exception: ListUser(**kwargs) @@ -57,7 +57,7 @@ def test_array_content_type_validation(test_domain): for kwargs in [ {"email": "john.doe@gmail.com", "roles": ["ADMIN", "USER"]}, {"email": "john.doe@gmail.com", "roles": ["1", "2"]}, - {"email": "john.doe@gmail.com", "roles": [datetime.utcnow()]}, + {"email": "john.doe@gmail.com", "roles": [datetime.now(UTC)]}, ]: with pytest.raises(ValidationError) as exception: IntegerListUser(**kwargs) diff --git a/tests/adapters/model/sqlalchemy_model/sqlite/test_array_datatype.py b/tests/adapters/model/sqlalchemy_model/sqlite/test_array_datatype.py index dafd7484..a6b9c185 100644 --- a/tests/adapters/model/sqlalchemy_model/sqlite/test_array_datatype.py +++ b/tests/adapters/model/sqlalchemy_model/sqlite/test_array_datatype.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime import pytest @@ -60,7 +60,7 @@ def test_array_content_type_validation(test_domain): {"email": "john.doe@gmail.com", "roles": [1, 2]}, {"email": "john.doe@gmail.com", "roles": ["1", 2]}, {"email": "john.doe@gmail.com", "roles": [1.0, 2.0]}, - {"email": "john.doe@gmail.com", "roles": [datetime.utcnow()]}, + {"email": "john.doe@gmail.com", "roles": [datetime.now(UTC)]}, ]: with pytest.raises(ValidationError) as exception: ArrayUser(**kwargs) @@ -77,7 +77,7 @@ def test_array_content_type_validation(test_domain): for kwargs in [ {"email": "john.doe@gmail.com", "roles": ["ADMIN", "USER"]}, {"email": "john.doe@gmail.com", "roles": ["1", "2"]}, - {"email": "john.doe@gmail.com", "roles": [datetime.utcnow()]}, + {"email": "john.doe@gmail.com", "roles": [datetime.now(UTC)]}, ]: with pytest.raises(ValidationError) as exception: IntegerArrayUser(**kwargs) diff --git a/tests/adapters/model/sqlalchemy_model/sqlite/test_json_datatype.py b/tests/adapters/model/sqlalchemy_model/sqlite/test_json_datatype.py index 6c4a3a07..1b4851f8 100644 --- a/tests/adapters/model/sqlalchemy_model/sqlite/test_json_datatype.py +++ b/tests/adapters/model/sqlalchemy_model/sqlite/test_json_datatype.py @@ -1,16 +1,15 @@ -from datetime import datetime - import pytest from sqlalchemy import types as sa_types from protean import BaseAggregate from protean.fields import DateTime, Dict, String +from protean.utils import utcnow_func class Event(BaseAggregate): name = String(max_length=255) - created_at = DateTime(default=datetime.utcnow()) + created_at = DateTime(default=utcnow_func) payload = Dict() diff --git a/tests/adapters/repository/sqlalchemy_repo/postgresql/test_associations.py b/tests/adapters/repository/sqlalchemy_repo/postgresql/test_associations.py index 6451f003..d552b9ac 100644 --- a/tests/adapters/repository/sqlalchemy_repo/postgresql/test_associations.py +++ b/tests/adapters/repository/sqlalchemy_repo/postgresql/test_associations.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime import pytest @@ -36,7 +36,7 @@ def test_updating_a_has_many_association(test_domain): post_repo = test_domain.repository_for(Post) post = Post(content="bar") - post.add_comments(Comment(content="foo", added_on=datetime.utcnow())) + post.add_comments(Comment(content="foo", added_on=datetime.now(UTC))) post_repo.add(post) with UnitOfWork(): diff --git a/tests/adapters/repository/sqlalchemy_repo/postgresql/test_persistence.py b/tests/adapters/repository/sqlalchemy_repo/postgresql/test_persistence.py index e4d39180..1e2df56c 100644 --- a/tests/adapters/repository/sqlalchemy_repo/postgresql/test_persistence.py +++ b/tests/adapters/repository/sqlalchemy_repo/postgresql/test_persistence.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime import pytest @@ -8,7 +8,7 @@ class Event(BaseAggregate): name = String(max_length=255) - created_at = DateTime(default=datetime.utcnow()) + created_at = DateTime(default=datetime.now(UTC)) payload = Dict() diff --git a/tests/aggregate/test_as_dict.py b/tests/aggregate/test_as_dict.py index 3c73b098..b1447081 100644 --- a/tests/aggregate/test_as_dict.py +++ b/tests/aggregate/test_as_dict.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime from protean import BaseAggregate, BaseEntity, BaseValueObject from protean.fields import ( @@ -11,6 +11,7 @@ Text, ValueObject, ) +from protean.utils import utcnow_func class TestAggregateWithNoEnclosedEntitiesOrValueObjects: @@ -34,9 +35,9 @@ class Post(BaseAggregate): title = String(required=True, max_length=1000) slug = String(required=True, max_length=1024) content = Text(required=True) - posted_at = DateTime(required=True, default=datetime.utcnow) + posted_at = DateTime(required=True, default=utcnow_func) - current_time = datetime.utcnow() + current_time = datetime.now(UTC) post = Post( title="Test Post", slug="test-post", diff --git a/tests/entity/test_entity_meta.py b/tests/entity/test_entity_meta.py index ce2d4af1..023c7021 100644 --- a/tests/entity/test_entity_meta.py +++ b/tests/entity/test_entity_meta.py @@ -93,3 +93,6 @@ def test_entity_meta_has_attributes_on_construction(self): "age", "id", ] # `relative_of` is ignored + + def test_meta_equality(self): + assert Person.meta_ != PersonAutoSSN.meta_ diff --git a/tests/event_store/test_reading_all_streams.py b/tests/event_store/test_reading_all_streams.py index 4a22ea71..b63c51b5 100644 --- a/tests/event_store/test_reading_all_streams.py +++ b/tests/event_store/test_reading_all_streams.py @@ -1,12 +1,12 @@ from __future__ import annotations -from datetime import datetime from uuid import uuid4 import pytest from protean import BaseEvent, BaseEventSourcedAggregate from protean.fields import DateTime, Identifier, String, Text +from protean.utils import utcnow_func class User(BaseEventSourcedAggregate): @@ -53,7 +53,7 @@ class Meta: class Published(BaseEvent): id = Identifier(required=True) - published_time = DateTime(default=datetime.utcnow) + published_time = DateTime(default=utcnow_func) class Meta: aggregate_cls = Post diff --git a/tests/field/test_identifier.py b/tests/field/test_identifier.py index fe1e6d9b..86317d5a 100644 --- a/tests/field/test_identifier.py +++ b/tests/field/test_identifier.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime from uuid import UUID, uuid4 import pytest @@ -38,7 +38,7 @@ def test_string_identifiers_are_preserved_as_strings_in_as_dict(): def test_that_only_ints_or_strings_are_allowed_in_identifiers(): identifier = Identifier() - invalid_values = [42.0, {"a": 1}, ["a", "b"], True, datetime.utcnow()] + invalid_values = [42.0, {"a": 1}, ["a", "b"], True, datetime.now(UTC)] for value in invalid_values: with pytest.raises(ValidationError): identifier._load(value) diff --git a/tests/subscription/test_message_filtering_with_origin_stream.py b/tests/subscription/test_message_filtering_with_origin_stream.py index e02fd359..6f334ad5 100644 --- a/tests/subscription/test_message_filtering_with_origin_stream.py +++ b/tests/subscription/test_message_filtering_with_origin_stream.py @@ -1,6 +1,6 @@ from __future__ import annotations -from datetime import datetime +from datetime import UTC, datetime from uuid import uuid4 import mock @@ -85,10 +85,10 @@ async def test_message_filtering_for_event_handlers_with_defined_origin_stream( user, Registered(id=identifier, email="john.doe@gmail.com", name="John Doe") ), Message.to_aggregate_event_message( - user, Activated(id=identifier, activated_at=datetime.utcnow()) + user, Activated(id=identifier, activated_at=datetime.now(UTC)) ), Message.to_aggregate_event_message( - email, Sent(email="john.doe@gmail.com", sent_at=datetime.utcnow()) + email, Sent(email="john.doe@gmail.com", sent_at=datetime.now(UTC)) ), ] messages[2].metadata.origin_stream_name = f"user-{identifier}" diff --git a/tests/subscription/test_no_message_filtering.py b/tests/subscription/test_no_message_filtering.py index 28633166..10b59c38 100644 --- a/tests/subscription/test_no_message_filtering.py +++ b/tests/subscription/test_no_message_filtering.py @@ -1,6 +1,6 @@ from __future__ import annotations -from datetime import datetime +from datetime import UTC, datetime from uuid import uuid4 import mock @@ -85,10 +85,10 @@ async def test_no_filtering_for_event_handlers_without_defined_origin_stream( user, Registered(id=identifier, email="john.doe@gmail.com", name="John Doe") ), Message.to_aggregate_event_message( - user, Activated(id=identifier, activated_at=datetime.utcnow()) + user, Activated(id=identifier, activated_at=datetime.now(UTC)) ), Message.to_aggregate_event_message( - email, Sent(email="john.doe@gmail.com", sent_at=datetime.utcnow()) + email, Sent(email="john.doe@gmail.com", sent_at=datetime.now(UTC)) ), ] messages[2].metadata.origin_stream_name = f"user-{identifier}" diff --git a/tests/test_aggregates.py b/tests/test_aggregates.py index 30f324d0..5d7f8b57 100644 --- a/tests/test_aggregates.py +++ b/tests/test_aggregates.py @@ -1,4 +1,4 @@ -from datetime import date, datetime +from datetime import date import pytest @@ -6,7 +6,7 @@ from protean.exceptions import IncorrectUsageError, ValidationError from protean.fields import Date, DateTime, HasMany, Reference, String from protean.reflection import declared_fields -from protean.utils import fully_qualified_name +from protean.utils import fully_qualified_name, utcnow_func class TestAggregateRegistration: @@ -59,8 +59,8 @@ class Person: def test_that_abstract_aggregates_do_not_have_id_field(self, test_domain): @test_domain.aggregate class TimeStamped: - created_at = DateTime(default=datetime.utcnow) - updated_at = DateTime(default=datetime.utcnow) + created_at = DateTime(default=utcnow_func) + updated_at = DateTime(default=utcnow_func) class Meta: abstract = True diff --git a/tests/test_other.py b/tests/test_other.py deleted file mode 100644 index 651b731a..00000000 --- a/tests/test_other.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Module to test other Protean Functions and Utilities""" - -import pytest - -from protean.core import entity -from protean.utils.importlib import perform_import - - -def test_perform_import(): - """Test the perform import function""" - - # Test importing of None - mod = perform_import(None) - assert mod is None - - # Test import of string - mod = perform_import("protean.core.entity") - assert mod == entity - - # Test import list - mod = perform_import(["protean.core.entity.BaseEntity"]) - assert mod == [entity.BaseEntity] - - # Test Failed import - with pytest.raises(ImportError): - perform_import("protean.core.entity.xxxx") - - # Test Direct import - mod = perform_import(entity) - assert mod == entity diff --git a/tests/unit_of_work/test_nested_inline_event_processing.py b/tests/unit_of_work/test_nested_inline_event_processing.py index 70152ffc..579756b7 100644 --- a/tests/unit_of_work/test_nested_inline_event_processing.py +++ b/tests/unit_of_work/test_nested_inline_event_processing.py @@ -1,6 +1,5 @@ from __future__ import annotations -from datetime import datetime from uuid import uuid4 import pytest @@ -17,13 +16,14 @@ from protean.fields import DateTime, Identifier, String, Text from protean.fields.basic import Boolean from protean.globals import current_domain +from protean.utils import utcnow_func published_count = 0 class Published(BaseEvent): id = Identifier(required=True) - published_time = DateTime(default=datetime.utcnow) + published_time = DateTime(default=utcnow_func) class Post(BaseEventSourcedAggregate): diff --git a/tests/utils/test_get_version.py b/tests/utils/test_get_version.py index b196f512..65e72187 100644 --- a/tests/utils/test_get_version.py +++ b/tests/utils/test_get_version.py @@ -1,7 +1,7 @@ -import pkg_resources +import importlib from protean.utils import get_version def test_get_version(): - assert get_version() == pkg_resources.require("protean")[0].version + assert get_version() == importlib.metadata.version("protean") diff --git a/tests/utils/test_import_from_full_path.py b/tests/utils/test_import_from_full_path.py index 843c746f..336de7a6 100644 --- a/tests/utils/test_import_from_full_path.py +++ b/tests/utils/test_import_from_full_path.py @@ -1,6 +1,6 @@ import pytest -from protean.utils.importlib import import_from_full_path +from protean.utils import import_from_full_path def test_that_a_domain_can_be_imported_from_a_full_path(): diff --git a/tests/utils/test_util.py b/tests/utils/test_util.py new file mode 100644 index 00000000..7b29c8d6 --- /dev/null +++ b/tests/utils/test_util.py @@ -0,0 +1,13 @@ +import datetime + +from protean.utils import utcnow_func + + +def test_utcnow_func(): + func = utcnow_func + assert func is not None + assert callable(func) is True + + result = func() + assert result is not None + assert type(result) == datetime.datetime diff --git a/tests/value_object/test_to_dict.py b/tests/value_object/test_to_dict.py index a8829191..70314666 100644 --- a/tests/value_object/test_to_dict.py +++ b/tests/value_object/test_to_dict.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime from protean import BaseAggregate from protean.core.value_object import BaseValueObject @@ -39,12 +39,12 @@ def test_embedded_simple_vo(self): assert simple.to_dict() == {"id": 12, "vo": {"foo": "foo", "bar": "bar"}} def test_datetime_vo_dict(self): - now = datetime.utcnow() + now = datetime.now(UTC) vo = VOWithDateTime(foo="foo", now=now) assert vo.to_dict() == {"foo": "foo", "now": str(now)} def test_embedded_datetime_vo(self): - now = datetime.utcnow() + now = datetime.now(UTC) vo = VOWithDateTime(foo="foo", now=now) simple = EntityWithDateTimeVO(id=12, vo=vo) assert simple.to_dict() == {"id": 12, "vo": {"foo": "foo", "now": str(now)}}