pyproject.toml (481 lines of code) (raw):
####################
# Project Metadata #
####################
[project]
description = "Database Migration Assessment"
license = { text = "Apache-2.0" }
name = "dma"
readme = "README.md"
requires-python = ">=3.9"
version = "4.3.44"
# keywords for easier look-up on PyPI
authors = [
{ name = "Cody Fincher", email = "codyfincher@google.com" },
{ name = "Warren Puziewicz", email = "wpuziewicz@google.com" },
{ name = "Steve Senior", email = "stevesenior@google.com" },
{ name = "Shane Borden", email = "shaneborden@google.com" },
{ name = "Eri Santos", email = "erisantos@google.com" },
]
keywords = [
"database",
"migration",
"assessment",
"postgres",
"mysql",
"oracle",
"mssql",
"duckdb",
"litestar",
"dma",
"granian",
"gcp",
"google",
"alloydb",
"mariadb",
]
maintainers = [
{ name = "Cody Fincher", email = "codyfincher@google.com" },
{ name = "Warren Puziewicz", email = "wpuziewicz@google.com" },
{ name = "Steve Senior", email = "stevesenior@google.com" },
{ name = "Shane Borden", email = "shaneborden@google.com" },
]
# options under https://pypi.org/classifiers/
classifiers = [ # ToDo: Modify according to your needs!
"Development Status :: 2 - Pre-Alpha",
"Programming Language :: Python",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
]
# direct dependencies of this package, installed when users `pip install dma` later.
dependencies = [
"duckdb>=1.0.0",
"polars[pyarrow]>=0.20.0",
"aiosql>=9.4",
"click>=8.1.7",
"rich-click>=1.7.3",
"jinja2>=3.1.3",
"sqlalchemy>=2.0.25",
"typing-extensions>=4.0.0",
"msgspec",
"greenlet",
"anyio"
]
[project.urls]
# important URLs for this project
Documentation = "https://googlecloudplatform.github.io/database-assessment/"
Issues = "https://github.com/GoogleCloudPlatform/database-assessment/issues"
Source = "https://github.com/GoogleCloudPlatform/database-assessment"
[project.optional-dependencies]
mssql = ["aioodbc"]
mysql = ["asyncmy>=0.2.9"]
oracle = ["oracledb"]
postgres = ["psycopg[pool,binary]"]
server = ["litestar[structlog,jinja]>=2.7.0", "litestar-granian>=0.2.3"]
[project.scripts]
dma = "dma.cli.main:app"
######################
# Build & Versioning #
######################
[build-system]
build-backend = "hatchling.build"
requires = ["hatchling"]
[tool.hatch.metadata]
# direct dependency references, e.g `pip @ git+https://github.com/pypa/pip.git@master`
allow-direct-references = true
[tool.hatch.build]
dev-mode-dirs = ["src/"]
sources = ["src"]
[tool.hatch.build.targets.sdist]
exclude = ["/.github", "/docs"]
# [tool.hatch.build.targets.app]
# python = "3.12"
#####################
# Environment Setup #
#####################
# Default environment with production dependencies
[tool.hatch.envs.default]
extra-dependencies = [
"bump2version",
"nodeenv",
# required test dependencies
"psycopg[binary]",
"pymysql",
"pymssql",
"oracledb",
"cython",
"anyio",
"coverage[toml]>=6.2",
"pytest",
"pytest-cov",
"pytest-mock",
"pytest-sugar",
"pytest-click",
"pytest-xdist",
"pytest-databases<=0.10.0",
# lint
"mypy",
"ruff<0.10.0",
"pylint",
"pre-commit",
"types-click",
"types-six",
"types-decorator",
"types-pyyaml",
"types-setuptools",
"asyncpg-stubs",
"pyyaml>=6",
# docs
"mkdocs-include-markdown-plugin",
"mkdocs-gen-files",
"mkdocs-literate-nav",
"mkdocs-section-index",
"mkdocstrings[python]",
"mkdocs-glightbox>=0.3.0",
"mkdocs-material[git,imaging,recommended]",
"mkdocs-minify-plugin",
"mike>=2.0.0",
# Extensions
"pymdown-extensions>=10.5",
# Necessary for syntax highlighting in code blocks
"pygments>=2.13",
# Validation
"linkchecker>=10.3",
# cli database browser
"harlequin",
]
installer = "uv"
[tool.hatch.envs.default.scripts]
upgrade-all = "PIP_COMPILE_UPGRADE=1 hatch env run --env {env_name} -- python --version"
upgrade-pkg = "PIP_COMPILE_UPGRADE='{args}' hatch env run --env {env_name} -- python --version"
# Test environment with test-only dependencies
[tool.hatch.envs.test]
features = ["server", "oracle", "mysql", "mssql", "postgres"]
template = "default"
type = "virtual"
# Test matrix for various Python versions replacing the functionality of tox
[[tool.hatch.envs.test.matrix]]
python = ["3.9", "3.10", "3.11", "3.12", "3.13"]
[tool.hatch.envs.test.scripts]
cov = "pytest --cov-report=term-missing --cov=src/dma --cov=tests {args}"
debug = "cov --no-cov -s --pdb --pdbcls=IPython.core.debugger:Pdb {args}"
no-cov = "cov --no-cov {args}"
# Docs environment
[tool.hatch.envs.docs]
features = ["server", "oracle", "mysql", "mssql", "postgres"]
template = "default"
type = "virtual"
[tool.hatch.envs.docs.env-vars]
MKDOCS_CONFIG = "mkdocs.yml"
PYTHONUNBUFFERED = "1"
SOURCE_DATE_EPOCH = "1580601600"
[tool.hatch.envs.docs.scripts]
build = "mkdocs build --config-file {env:MKDOCS_CONFIG} --clean --strict {args}"
ci-build = "mike deploy --config-file {env:MKDOCS_CONFIG} --update-aliases {args}"
serve = "mkdocs serve --config-file {env:MKDOCS_CONFIG} --dev-addr localhost:8000 {args}"
# --ignore-url=None since the SUMMARY.md file leaves a <toc>None</toc> in sitemap.xml
validate = "linkchecker --config .linkcheckerrc --ignore-url=/reference --ignore-url=None site"
# https://github.com/linkchecker/linkchecker/issues/678
build-check = ["build", "validate"]
[tool.hatch.envs.local]
features = ["server", "oracle", "mysql", "mssql", "postgres"]
lock-filename = "requirements/requirements-dev.txt"
path = ".venv/"
python = "3.12"
template = "default"
type = "virtual"
[tool.hatch.envs.local.scripts]
start-infra = "docker-compose -f tests/docker-compose.yml up --force-recreate -d"
stop-infra = "docker-compose -f tests/docker-compose.yml down --remove-orphans --volumes"
# Lint environment
[tool.hatch.envs.lint]
features = ["server", "oracle", "mysql", "mssql", "postgres"]
python = "3.9"
template = "default"
type = "virtual"
[tool.hatch.envs.lint.scripts]
check = ["style", "typing"]
fix = [
"ruff format {args:.}",
"ruff check --fix --unsafe-fixes {args:.}",
"typing",
"style", # feedback on what is not fixable
]
style = ["echo \"VERSION: `ruff --version`\"", "ruff check {args:.}", "ruff format --check {args:.}"]
typing = ["echo \"VERSION: `mypy --version`\"", "mypy --install-types --non-interactive {args}"]
##################
# External Tools #
##################
## Linting Tools
[tool.slotscheck]
strict-imports = false
[tool.codespell]
ignore-words-list = "alog"
skip = 'pdm.lock, package-lock.json'
[tool.pyright]
autoSearchPaths = false
exclude = ["docs/**/*.py", "tests/**/*.py", "scripts/**/*.py"]
extraPaths = [] # Include paths from PYTHONPATH env var and .env definition
include = ["src/dma", "tests"]
pythonVersion = "3.9"
reportMissingImports = "information"
reportMissingTypeStubs = "information"
reportUnknownMemberType = "none"
reportUnnecessaryTypeIgnoreComments = true
root = ['src']
typeCheckingMode = "standard"
useLibraryCodeForTypes = true
[tool.mypy]
disallow_untyped_defs = false
files = ["src/dma", "tests"]
follow_imports = "normal" # "silent" for not following
ignore_missing_imports = true
pretty = true
show_column_numbers = true
warn_no_return = false
warn_unused_ignores = true
[[tool.mypy.overrides]]
disallow_untyped_decorators = false
module = ["tests.*"]
warn_unused_ignores = false
[[tool.mypy.overrides]]
disable_error_code = ["arg-type"]
disallow_untyped_calls = false
disallow_untyped_decorators = false
module = ["aisoql.*"]
[[tool.mypy.overrides]]
disallow_untyped_calls = false
disallow_untyped_defs = false
module = ["dma.lib.db.adapters.*", "dma.lib.db.adapters", "dma.lib.db.base", "dma.lib.db"]
warn_unused_ignores = false
[[tool.mypy.overrides]]
ignore_missing_imports = true
module = ["sqlalchemy.*", "nodeenv"]
[tool.ruff]
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".hg",
".mypy_cache",
".nox",
".pants.d",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
'__pycache__',
]
fix = true
include = ["src/**/*.py", "src/**/*.pyi", "tests/**/*.py", "tests/**/*.pyi"]
line-length = 120
lint.fixable = ["ALL"]
lint.ignore = [
"B027", # Allow non-abstract empty methods in abstract base classes
"FBT003", # Allow boolean positional values in function calls, like `dict.get(... True)`
# Ignore checks for possible passwords
"S105",
"S106",
"S107",
# Ignore complexity
"C901",
"PLR0911",
"PLR0912",
"PLR0913",
"PLR0915",
"PLC1901", # empty string comparisons
"PLW2901", # `for` loop variable overwritten
"SIM114", # Combine `if` branches using logical `or` operator
"E203", # Whitespace before :, needed for black compatability and also `ruff format`
"ISC001", # causes unexpected behaviour with formatter
"E501", # pycodestyle line too long, handled by black
"D100", # pydocstyle - missing docstring in public module
"D101", # pydocstyle - missing docstring in public class
"D102", # pydocstyle - missing docstring in public method
"D103", # pydocstyle - missing docstring in public function
"D104", # pydocstyle - missing docstring in public package
"D105", # pydocstyle - missing docstring in magic method
"D106", # pydocstyle - missing docstring in public nested class
"D107", # pydocstyle - missing docstring in __init__
"D202", # pydocstyle - no blank lines allowed after function docstring
"D205", # pydocstyle - 1 blank line required between summary line and description
"D415", # pydocstyle - first line should end with a period, question mark, or exclamation point
"UP037", # pyupgrade - removes quotes from type annotation
"A003", # flake8-builtins - class attribute {name} is shadowing a python builtin
"B010", # flake8-bugbear - do not call setattr with a constant attribute value
"B008", # flake8-bugbear - Do not perform function call `Parameter` in argument defaultsRuff(B008)
"RUF012", # ruff - mutable class attributes should be annotated with `typing.ClassVar`
"ANN401", # ruff - Dynamically typed expressions (typing.Any) are disallowed
"PLR0913", # ruff - Too many arguments to function call
"PLR2004", # Magic value used in comparison
"FBT001", # Boolean typed positional argument in function definition
"FBT002", # Boolean default positional argument in function definition
"FBT003", # Boolean Boolean default positional argument in function definition
"ARG002", # Unused method argument
"ARG001", # Unused function argument
"TD002",
"TD003",
"FIX002",
"PGH003",
"RUF006",
"SLF001",
"PT007",
'S603',
"E501", # pycodestyle line too long, handled by black
"PLW2901", # pylint - for loop variable overwritten by assignment target
"ANN401",
"FBT",
"PLR0913", # too many arguments
"PT",
"TD",
"PERF203", # ignore for now; investigate
"COM812",
"PLR0917",
"DOC201", # `return` is not documented in docstring
"DOC501", # Raised exception missing from docstring
"DOC502", # Raised exception missing from docstring
"A005", # module shadows builtin
"DOC402", # Yield missing from docstring
]
lint.select = ["ALL"]
# Allow unused variables when underscore-prefixed.
lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
preview = true # preview features & checks, use with caution
src = ["src", "tests/", "scripts/", "docs/", "notebooks/"]
target-version = "py39"
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.ruff.lint.isort]
known-first-party = ["dma", "tests", "scripts"]
[tool.ruff.lint.flake8-tidy-imports]
ban-relative-imports = "all"
[tool.ruff.lint.per-file-ignores]
# Allow print/pprint
"docs/*" = ["S404", "INP001"]
"examples/*" = ["T201"]
# Tests can use magic values, assertions, and relative imports
"__init__.py" = ['F401', 'D104']
"__main__.py" = ["E402"]
"scripts/*.py" = ["INP001", "S404"]
"scripts/masker/dma-collection-masker" = [
"PYI024",
"FA100",
"S202",
"S311",
"UP006",
"UP035",
"FA102",
"EM101",
"TRY003",
"PLR6301",
]
"scripts/post_build.py" = ["ERA001"]
"scripts/pre_build.py" = ["ERA001"]
"tests/**/*" = [
"PLR2004",
"S101",
"TID252",
"ERA001",
"ANN201",
"ANN001",
"D103",
"D104",
"T201",
"S404",
"PLR6301",
"PLC2701",
"FA102",
]
# temp
"src/dma/lib/db/adapters/*.py" = ["ANN001", "ANN201", "ANN204", "PLR6301"]
# copyright
"**/*.py" = ["CPY001", 'RUF029']
[tool.ruff.lint.mccabe]
max-complexity = 12
[tool.ruff.lint.pep8-naming]
classmethod-decorators = [
"classmethod",
"sqlalchemy.ext.declarative.declared_attr",
"sqlalchemy.orm.declared_attr.directive",
"sqlalchemy.orm.declared_attr",
]
# configure in-case someone runs this
[tool.black]
exclude = '''
/(
\.git
| \.mypy_cache
| \.tox
| venv
| \.venv
| _build
| buck-out
| build
| dist
)/
'''
include = '\.pyi?$'
line-length = 120
## Testing Tools
[tool.pytest.ini_options]
addopts = "-n 2 --dist=loadgroup -ra -q --doctest-glob='*.md'"
filterwarnings = [
"ignore::DeprecationWarning:aiosql.*",
"ignore::DeprecationWarning:pkg_resources",
"ignore::DeprecationWarning:xdist.*",
]
markers = [
"integration: Integration Tests",
"unit: Unit Tests",
"mysql: MySQL Tests",
"postgres: Postgres Tests",
"oracle: Oracle Tests",
"spanner: Google Cloud Spanner Tests",
"duckdb: DuckDB Tests",
"mssql: Microsoft SQL Server Tests",
"server: Web server and API Tests",
]
minversion = "6.0"
testpaths = ["tests"]
[tool.coverage.run]
branch = true
concurrency = ["multiprocessing"]
omit = [
"_version.py", # automatically created by hatch-vcs, not in repo
"src/dma/__about__.py",
"tests/*",
"scripts/*",
]
parallel = true
source = ["src/dma"]
[tool.coverage.paths]
source = ["src/", "*/site-packages/"]
[tool.coverage.report]
# Regexes for lines to exclude from consideration
exclude_lines = [
# Have to re-enable the standard pragma
"pragma: no cover",
# Don't complain about missing debug-only code:
"def __repr__",
"if self\\.debug",
# Don't complain if tests don't hit defensive assertion code:
"raise AssertionError",
"raise NotImplementedError",
# Don't complain if non-runnable code isn't run:
"if 0:",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
'class .*\bProtocol\):',
'@(abc\.)?abstractmethod',
]