diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..c2bfa9d --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +ignore = ANN101 +max-complexity = 10 +exclude = p38venv,.pytest_cache diff --git a/orm/__init__.py b/orm/__init__.py index 1ceb7b0..c947954 100644 --- a/orm/__init__.py +++ b/orm/__init__.py @@ -1,7 +1,7 @@ -from orm.fields import Integer, BigInteger, Boolean, Time, Text, String, JSON, DateTime, Date, Decimal, Float, \ - ForeignKey +from orm.exceptions import ModelDefinitionError, ModelNotSet, MultipleMatches, NoMatch +from orm.fields import BigInteger, Boolean, Date, DateTime, Decimal, Float, ForeignKey, Integer, JSON, String, Text, \ + Time from orm.models import Model -from orm.exceptions import ModelDefinitionError, MultipleMatches, NoMatch, ModelNotSet __version__ = "0.0.1" __all__ = [ @@ -17,5 +17,9 @@ __all__ = [ "Decimal", "Float", "ForeignKey", - "Model" + "Model", + "ModelDefinitionError", + "ModelNotSet", + "MultipleMatches", + "NoMatch" ] diff --git a/orm/fields.py b/orm/fields.py index aa40930..80814f0 100644 --- a/orm/fields.py +++ b/orm/fields.py @@ -1,9 +1,9 @@ import datetime import decimal -from typing import Optional, List, Type, TYPE_CHECKING +from typing import List, Optional, TYPE_CHECKING, Type, Any, Union import sqlalchemy -from pydantic import Json +from pydantic import Json, BaseModel from pydantic.fields import ModelField import orm @@ -22,9 +22,7 @@ class BaseField: if args: if isinstance(args[0], str): if name is not None: - raise ModelDefinitionError( - 'Column name cannot be passed positionally and as a keyword.' - ) + raise ModelDefinitionError('Column name cannot be passed positionally and as a keyword.') name = args.pop(0) self.name = name @@ -43,20 +41,20 @@ class BaseField: raise ModelDefinitionError('Primary key column cannot be pydantic only.') @property - def is_required(self): + def is_required(self) -> bool: return not self.nullable and not self.has_default and not self.is_auto_primary_key @property - def default_value(self): + def default_value(self) -> Any: default = self.default if self.default is not None else self.server_default return default() if callable(default) else default @property - def has_default(self): + def has_default(self) -> bool: return self.default is not None or self.server_default is not None @property - def is_auto_primary_key(self): + def is_auto_primary_key(self) -> bool: if self.primary_key: return self.autoincrement return False @@ -83,7 +81,7 @@ class BaseField: def get_constraints(self) -> Optional[List]: return [] - def expand_relationship(self, value, child): + def expand_relationship(self, value, child) -> Any: return value @@ -95,70 +93,70 @@ class String(BaseField): self.length = kwargs.pop('length') super().__init__(*args, **kwargs) - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: return sqlalchemy.String(self.length) class Integer(BaseField): __type__ = int - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: return sqlalchemy.Integer() class Text(BaseField): __type__ = str - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: return sqlalchemy.Text() class Float(BaseField): __type__ = float - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: return sqlalchemy.Float() class Boolean(BaseField): __type__ = bool - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: return sqlalchemy.Boolean() class DateTime(BaseField): __type__ = datetime.datetime - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: return sqlalchemy.DateTime() class Date(BaseField): __type__ = datetime.date - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: return sqlalchemy.Date() class Time(BaseField): __type__ = datetime.time - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: return sqlalchemy.Time() class JSON(BaseField): __type__ = Json - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: return sqlalchemy.JSON() class BigInteger(BaseField): __type__ = int - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: return sqlalchemy.BigInteger() @@ -172,11 +170,11 @@ class Decimal(BaseField): self.precision = kwargs.pop('precision') super().__init__(*args, **kwargs) - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: return sqlalchemy.DECIMAL(self.length, self.precision) -def create_dummy_instance(fk: Type['Model'], pk: int = None): +def create_dummy_instance(fk: Type['Model'], pk: int = None) -> 'Model': init_dict = {fk.__pkname__: pk or -1} init_dict = {**init_dict, **{k: create_dummy_instance(v.to) for k, v in fk.__model_fields__.items() @@ -192,18 +190,18 @@ class ForeignKey(BaseField): self.to = to @property - def __type__(self): + def __type__(self) -> Type[BaseModel]: return self.to.__pydantic_model__ - def get_constraints(self): + def get_constraints(self) -> List[sqlalchemy.schema.ForeignKey]: fk_string = self.to.__tablename__ + "." + self.to.__pkname__ return [sqlalchemy.schema.ForeignKey(fk_string)] - def get_column_type(self): + def get_column_type(self) -> sqlalchemy.Column: to_column = self.to.__model_fields__[self.to.__pkname__] return to_column.get_column_type() - def expand_relationship(self, value, child): + def expand_relationship(self, value, child) -> Union['Model', List['Model']]: if not isinstance(value, (self.to, dict, int, str, list)) or ( isinstance(value, orm.models.Model) and not isinstance(value, self.to)): raise RelationshipInstanceError( diff --git a/orm/queryset.py b/orm/queryset.py index 63ba69a..36160e8 100644 --- a/orm/queryset.py +++ b/orm/queryset.py @@ -75,7 +75,6 @@ class QuerySet: to_table = model_cls.__table__.name alias = model_cls._orm_relationship_manager.resolve_relation_join(join_params.from_table, to_table) - # print(f'resolving tables alias from {join_params.from_table}, to: {to_table} -> {alias}') if alias not in self.used_aliases: if join_params.prev_model.__model_fields__[part].virtual: to_key = next((v for k, v in model_cls.__model_fields__.items() @@ -110,30 +109,19 @@ class QuerySet: def extract_auto_required_relations(self, join_params: JoinParameters, rel_part: str = '', nested: bool = False, parent_virtual: bool = False): - # print(f'checking model {join_params.prev_model}, {rel_part}') for field_name, field in join_params.prev_model.__model_fields__.items(): - # print(f'checking_field {field_name}') if self.field_is_a_foreign_key_and_no_circular_reference(field, field_name, rel_part): rel_part = field_name if not rel_part else rel_part + '__' + field_name if not field.nullable: - # print(f'field {field_name} is not nullable, appending to auto, curr rel: {rel_part}') if rel_part not in self._select_related: self.auto_related.append("__".join(rel_part.split("__")[:-1])) rel_part = '' elif self.field_qualifies_to_deeper_search(field, parent_virtual, nested, rel_part): - # print( - # f'field {field_name} is nullable, going down, curr rel: ' - # f'{rel_part}, nested:{nested}, virtual:{field.virtual}, virtual_par:{parent_virtual}, ' - # f'injoin: {"__".join(rel_part.split("__")[:-1]) in self._select_related}') join_params = JoinParameters(field.to, join_params.previous_alias, join_params.from_table, join_params.prev_model) self.extract_auto_required_relations(join_params=join_params, rel_part=rel_part, nested=True, parent_virtual=field.virtual) else: - # print( - # f'field {field_name} is out, going down, curr rel: ' - # f'{rel_part}, nested:{nested}, virtual:{field.virtual}, virtual_par:{parent_virtual}, ' - # f'injoin: {"__".join(rel_part.split("__")[:-1]) in self._select_related}') rel_part = '' def build_select_expression(self): diff --git a/orm/relations.py b/orm/relations.py index a1a6625..a9a6971 100644 --- a/orm/relations.py +++ b/orm/relations.py @@ -5,8 +5,6 @@ from random import choices from typing import TYPE_CHECKING, List from weakref import proxy -from sqlalchemy import text - from orm.fields import ForeignKey if TYPE_CHECKING: # pragma no cover @@ -55,11 +53,6 @@ class RelationshipManager: child, parent = parent, proxy(child) else: child = proxy(child) - # print( - # f'setting up relationship, {parent_id}, {child_id}, ' - # f'{parent.__class__.__name__}, {child.__class__.__name__}, ' - # f'{parent.pk if parent.values is not None else None}, ' - # f'{child.pk if child.values is not None else None}') parents_list = self._relations[parent_name.lower().title() + '_' + child_name + 's'].setdefault(parent_id, []) self.append_related_model(parents_list, child) children_list = self._relations[child_name.lower().title() + '_' + parent_name].setdefault(child_id, []) diff --git a/requirements.txt b/requirements.txt index db5ae51..fde4a73 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,10 @@ pytest pytest-cov codecov pytest-asyncio -fastapi \ No newline at end of file +fastapi +flake8 +flake8-black +flake8-bugbear +flake8-import-order +flake8-bandit +flake8-annotations \ No newline at end of file