Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

80 add typehints #107

Merged
merged 12 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,20 @@ install:

clean: FORCE
@printf "\n\033[36m--- $@: Clean ---\033[0m\n"
-rm -Rf .pytest_cache
-rm -rf .pytest_cache
-rm -f .coverage*
-rm -Rf dist
-rm -Rf build
-rm -Rf docs/build
-rm -Rf docs/source/generated/*
-rm -Rf htmlcov
-rm -rf .tox
-rm -rf dist
-rm -rf build
-rm -rf docs/build
-rm -rf docs/source/generated/*
-rm -rf htmlcov

clean-all: clean
@printf "\n\033[36m--- $@: Clean All---\033[0m\n"
-rm -f INSTALLED_FILES
-rm -f setuptools-*.egg
-rm -f use-distutils
-rm -Rf .tox
-rm -Rf dist
-rm -Rf src/$(PACKAGE_NAME).egg-info
-rm -Rf $(VIRTUALENV_DIR)

Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ include-package-data = true
where = ["src"]

[tool.setuptools.package-data]
stalker = ["VERSION"]
stalker = ["VERSION", "py.typed"]

[tool.setuptools.exclude-package-data]
stalker = ["alembic", "docs", "tests"]
Expand Down Expand Up @@ -106,12 +106,14 @@ env_list = ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
description = "run the tests with pytest"
package = "wheel"
wheel_build_env = ".pkg"
set_env = { SQLALCHEMY_WARN_20 = "1"}
set_env = { SQLALCHEMY_WARN_20 = "1" }
deps = [
"pytest>=6",
"pytest-cov",
"pytest-xdist",
]
commands = [
["pytest"],
]
]

[tool.mypy]
2 changes: 2 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ flake8-pep3101
flake8-spellcheck
flake8-pyproject
furo
mypy
pyglet
pytest
pytest-cov
Expand All @@ -21,4 +22,5 @@ sphinx-autoapi
# sphinx-findthedocs
tox
twine
types-pytz
wheel
3 changes: 1 addition & 2 deletions src/stalker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
"""
from stalker.version import __version__ # noqa: F401
from stalker import config, log # noqa: I100

if True:
defaults = config.Config()
defaults: config.Config = config.Config()
from stalker.models.asset import Asset
from stalker.models.auth import (
AuthenticationLog,
Expand Down
12 changes: 6 additions & 6 deletions src/stalker/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import datetime
import os
import sys
from typing import Any
from typing import Any, Dict

from stalker import log

Expand All @@ -16,11 +16,11 @@ class ConfigBase(object):
This is based on Sphinx's config idiom.
"""

default_config_values = {}
default_config_values: Dict[str, Any] = {}

def __init__(self):
def __init__(self) -> None:
self.config_values = self.default_config_values.copy()
self.user_config = {}
self.user_config: Dict[str, Any] = {}
self._parse_settings()

def _parse_settings(self) -> None:
Expand Down Expand Up @@ -87,7 +87,7 @@ def __getattr__(self, name: str) -> Any:
"""
return self.config_values[name]

def __getitem__(self, name) -> Any:
def __getitem__(self, name: str) -> Any:
"""Return item with the key.

Args:
Expand All @@ -98,7 +98,7 @@ def __getitem__(self, name) -> Any:
"""
return getattr(self, name)

def __setitem__(self, name, value) -> None:
def __setitem__(self, name: str, value: Any) -> None:
"""Set the item with index of name to value.

Args:
Expand Down
9 changes: 6 additions & 3 deletions src/stalker/db/declarative.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# -*- coding: utf-8 -*-
"""The declarative base class is situated here."""
import logging
from typing import Any, Type

from sqlalchemy.orm import declarative_base

from stalker.db.session import DBSession
from stalker.log import get_logger
from stalker.utils import make_plural

logger = get_logger(__name__)
logger: logging.Logger = get_logger(__name__)


class ORMClass(object):
Expand All @@ -15,7 +18,7 @@ class ORMClass(object):
query = DBSession.query_property()

@property
def plural_class_name(self):
def plural_class_name(self) -> str:
"""Return plural name of this class.

Returns:
Expand All @@ -24,4 +27,4 @@ def plural_class_name(self):
return make_plural(self.__class__.__name__)


Base = declarative_base(cls=ORMClass)
Base: Type[Any] = declarative_base(cls=ORMClass)
10 changes: 6 additions & 4 deletions src/stalker/db/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
This is a runtime storage for the DB session. Greatly simplifying the usage of a
scoped session.
"""
from typing import Any, List, TYPE_CHECKING, Union

from sqlalchemy.orm import scoped_session, sessionmaker

if TYPE_CHECKING: # pragma: no cover
from stalker.models.entity import SimpleEntity


class ExtendedScopedSession(scoped_session):
"""A customized scoped_session which adds new functionality."""

def save(self, data=None):
def save(self, data: Union[None, List[Any], "SimpleEntity"] = None) -> None:
"""Add and commits data at once.

Args:
Expand All @@ -25,7 +30,4 @@ def save(self, data=None):
self.commit()


# try:
# DBSession = ExtendedScopedSession(sessionmaker(extension=None))
# except TypeError:
DBSession = ExtendedScopedSession(sessionmaker(future=True))
65 changes: 39 additions & 26 deletions src/stalker/db/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
Whenever stalker.db or something under it imported, the :func:`stalker.db.setup` becomes
available to let one set up the database.
"""
import logging
import os
from typing import Any, Dict, List, Optional, Union

from sqlalchemy import Column, Table, Text, engine_from_config, text
from sqlalchemy.exc import IntegrityError, OperationalError, ProgrammingError
Expand All @@ -31,23 +33,23 @@
from stalker.db.session import DBSession


logger = log.get_logger(__name__)
logger: logging.Logger = log.get_logger(__name__)

# TODO: Try to get it from the API (it was not working inside a package before)
alembic_version = "feca9bac7d5a"
alembic_version: str = "feca9bac7d5a"


def setup(settings=None):
def setup(settings: Optional[Dict[str, Any]] = None) -> None:
"""Connect the system to the given database.

If the database is None then it sets up using the default database in the settings
file.
If the database is None then it sets up using the default database in the
settings file.

Args:
settings (dict): This is a dictionary which has keys prefixed with
"sqlalchemy" and shows the settings. The most important one is the
engine. The default is None, and in this case it uses the settings from
stalker.config.Config.database_engine_settings
settings (Dict[str, Any]): This is a dictionary which has keys prefixed
with "sqlalchemy" and shows the settings. The most important one is
the engine. The default is None, and in this case it uses the
settings from stalker.config.Config.database_engine_settings
"""
if settings is None:
settings = defaults.database_engine_settings
Expand Down Expand Up @@ -82,7 +84,7 @@ def setup(settings=None):
create_repo_vars()


def update_defaults_with_studio():
def update_defaults_with_studio() -> None:
"""Update the default values from Studio instance.

Update only if a database and a Studio instance is present.
Expand All @@ -96,7 +98,7 @@ def update_defaults_with_studio():
studio.update_defaults()


def init():
def init() -> None:
"""Fill the database with default values."""
logger.debug("initializing database")

Expand Down Expand Up @@ -210,15 +212,15 @@ def init():
logger.debug("finished initializing the database")


def create_repo_vars():
def create_repo_vars() -> None:
"""Create environment variables for all the repositories in the current database."""
# get all the repositories
all_repos = Repository.query.all()
for repo in all_repos:
os.environ[repo.env_var] = repo.path


def get_alembic_version():
def get_alembic_version() -> Union[None, str]:
"""Return the alembic version of the database.

Returns:
Expand All @@ -238,7 +240,7 @@ def get_alembic_version():
return None


def check_alembic_version():
def check_alembic_version() -> None:
"""Check the alembic version of the database.

Raises:
Expand All @@ -255,7 +257,7 @@ def check_alembic_version():
raise ValueError(f"Please update the database to version: {alembic_version}")


def create_alembic_table():
def create_alembic_table() -> None:
"""Create the default alembic_version table.

Also create the data so that any new database will be considered as the latest
Expand Down Expand Up @@ -301,11 +303,11 @@ def create_alembic_table():
logger.debug("alembic_version table is already there, not doing anything!")


def __create_admin__():
def __create_admin__() -> User:
"""Create the admin.

Returns:
stalker.models.auth.User: The admin user.
User: The admin user.
"""
logger.debug("creating the default administrator user")

Expand All @@ -317,6 +319,7 @@ def __create_admin__():
if not admin_department:
admin_department = Department(name=defaults.admin_department_name)
DBSession.add(admin_department)
DBSession.commit()
# create the admins group
admins_group = Group.query.filter_by(name=defaults.admin_group_name).first()

Expand All @@ -340,8 +343,11 @@ def __create_admin__():
groups=[admins_group],
)

admin.created_by = admin
admin.updated_by = admin
DBSession.add(admin)
DBSession.commit()

# admin.created_by = admin
# admin.updated_by = admin

# update the department as created and updated by admin user
admin_department.created_by = admin
Expand All @@ -356,7 +362,7 @@ def __create_admin__():
return admin


def create_ticket_statuses():
def create_ticket_statuses() -> None:
"""Create the default ticket statuses."""
# create as admin
admin = User.query.filter(User.login == defaults.admin_name).first()
Expand Down Expand Up @@ -403,8 +409,11 @@ def create_ticket_statuses():


def create_entity_statuses(
entity_type="", status_names=None, status_codes=None, user=None
):
entity_type: str = "",
status_names: Optional[List[str]] = None,
status_codes: Optional[List[str]] = None,
user: Optional[User] = None,
) -> None:
"""Create the default task statuses.

Args:
Expand Down Expand Up @@ -479,7 +488,7 @@ def create_entity_statuses(
DBSession.flush()


def register(class_):
def register(class_: Type) -> None:
"""Register the given class to the database.

It is mainly used to create the :class:`.Action` s needed for the
Expand All @@ -494,6 +503,7 @@ def register(class_):
hierarchy)::

from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from stalker.models.entity import SimpleEntity

class MyDataClass(SimpleEntity):
Expand All @@ -504,8 +514,11 @@ class MyDataClass(SimpleEntity):
__tablename__ = 'MyData'
__mapper_arguments__ = {'polymorphic_identity': 'MyData'}

my_data_id = Column('id', Integer, ForeignKey('SimpleEntities.c.id'),
primary_key=True)
my_data_id : Mapped[int] = mapped_column(
'id',
ForeignKey('SimpleEntities.c.id'),
primary_key=True,
)

Now because Stalker is using Pyramid authorization mechanism it needs to be
able to have an :class:`.Permission` about your new class, so you can
Expand All @@ -524,7 +537,7 @@ class MyDataClass(SimpleEntity):
to do the specified Action.

Args:
class_: The class itself that needs to be registered.
class_ (Type): The class itself that needs to be registered.

Raises:
TypeError: If the class_ arg is not a ``type`` instance.
Expand Down
Loading
Loading