[build-system] requires = ["setuptools>=68"] build-backend = "setuptools.build_meta" [project] name = "queryapigate" dynamic = ["version"] description = "Turn SQL into governed REST APIs over MySQL, PostgreSQL, ClickHouse, SQLite, DuckDB, H2, MongoDB, and Parquet/CSV/JSON files - JSON, CSV, Parquet or Excel out, with scoped keys, an admin Console and MCP for AI agents." readme = "README.md" requires-python = ">=3.11" license = "Apache-2.0" authors = [{ name = "Anantha Raju C", email = "arcswdev@gmail.com" }] keywords = ["sql", "api", "rest", "rest-api", "data-api", "flask", "database", "mysql", "postgresql", "clickhouse", "sqlite", "duckdb", "mongodb", "h2", "jdbc", "parquet", "s3", "mcp", "ai-agents"] classifiers = [ "Development Status :: 4 - Beta", "Environment :: Web Environment", "Framework :: Flask", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Database", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ] # Werkzeug is imported directly (ProxyFix, ClosingIterator), so it has its own floor. Flask 2.x can't be imported beside # Werkzeug 3, which pip installs with it; PyYAML 6.0 has no wheel for Python 3.12 and later, and doesn't build there. dependencies = ["Flask>=3.0", "Werkzeug>=3.0", "PyYAML>=6.0.1", "openpyxl>=3.1"] [project.optional-dependencies] mysql = ["mysql-connector-python>=8.0"] postgres = ["psycopg2-binary>=2.9.5"] # 2.9.5 is the first with wheels for Python 3.11, the oldest supported clickhouse = ["clickhouse-driver>=0.2"] h2 = ["JayDeBeApi>=1.2", "JPype1>=1.4"] # also needs a Java runtime duckdb = ["duckdb>=1.2"] # allowed_directories/allowed_paths, which lock every connection (duckfiles.py), arrived in 1.2 mongo = ["pymongo>=4.0"] trino = ["trino>=0.330"] # the trino DB-API client (runners._Trino); experimental, ADR 0026 flow = ["sqlglot>=25.2,<31"] # the Access tab's tables and joins (sqlflow.py) and column masking's tracing (masking.py) encryption = ["cryptography>=41"] # only needed when QUERYAPIGATE_SECRET_KEY is set - see store.py's _get_fernet() redis = ["redis>=5"] # only needed when QUERYAPIGATE_REDIS_URL is set - see rediscache.py mcp = ["mcp>=1.21.1,<2"] # only needed for `queryapigate mcp` - see mcp_server.py. Structured tool output arrived # in 1.10, but nothing before 1.21.1 can be imported beside the pydantic pip installs # with it (a private pydantic function they used is gone). jwt = ["PyJWT[crypto]>=2.14"] # only needed when QUERYAPIGATE_JWT_SECRET or QUERYAPIGATE_JWT_JWKS_URL is set - see jwtauth.py. # 2.14 is the floor because that is where PyJWKClient stops refetching the JWKS for # every unknown key id: before it, forged tokens could make the server hammer the provider. all = ["queryapigate[mysql,postgres,clickhouse,h2,duckdb,mongo,trino,flow,encryption,jwt]"] # only needed when an OTLP endpoint is set (OTEL_EXPORTER_OTLP_ENDPOINT) - see tracing.py otel = ["opentelemetry-sdk>=1.27", "opentelemetry-exporter-otlp-proto-http>=1.27"] server = ["gunicorn>=21"] # only needed for ${aws-sm:...} and aws-rds-iam credentials (ADR 0015) - see aws.py. 1.28 is the floor because that is # where boto3 reads AWS_ENDPOINT_URL_ (a VPC endpoint, or a local stand-in). aws = ["boto3>=1.28"] docs = ["mkdocs-material>=9,<10"] dev = ["queryapigate[all]", "queryapigate[redis]", "queryapigate[otel]", "queryapigate[aws]", "queryapigate[mcp]; python_version>='3.10'", "ruff>=0.5", "openapi-spec-validator>=0.7", "hypothesis>=6.100", "mypy>=1.10", "coverage>=7.4"] [project.urls] Homepage = "https://github.com/AnanthaRajuC/QueryAPIGate" Documentation = "https://AnanthaRajuC.github.io/QueryAPIGate/" Repository = "https://github.com/AnanthaRajuC/QueryAPIGate" Issues = "https://github.com/AnanthaRajuC/QueryAPIGate/issues" Changelog = "https://github.com/AnanthaRajuC/QueryAPIGate/blob/main/CHANGELOG.md" [project.scripts] queryapigate = "queryapigate.cli:main" [tool.setuptools.dynamic] version = { attr = "queryapigate.__version__" } [tool.setuptools.packages.find] include = ["queryapigate*"] [tool.setuptools.package-data] queryapigate = ["lib/*.jar", "console_dist/**/*"] [tool.ruff] line-length = 120 target-version = "py311" [tool.ruff.lint] select = ["E", "F", "W", "I", "B"] [tool.mypy] # Gradual typing: the codebase has no type hints yet, so this catches real static errors (like a subclass # assigning the wrong type to a base class's annotated attribute) without demanding annotations everywhere - # a starting point to tighten later, not a finished type-checked codebase. Checked against the oldest Python this # project supports (requires-python), so code that needs a newer one fails here, not on a user's machine. python_version = "3.11" ignore_missing_imports = true # none of the DB drivers ship type stubs # Belt and braces: mypy 1.x and 2.x disagree on whether ignore_missing_imports alone covers a module that's # installed but has no stubs/py.typed marker (PyYAML, openpyxl, ...) - the "import-untyped" error code, as # opposed to one mypy can't find at all ("import-not-found") - so silence that code explicitly too. Found by # CI running the oldest supported Python, which can resolve an older mypy than this project's own dev environment. disable_error_code = ["import-untyped"] warn_unused_ignores = true warn_redundant_casts = true [tool.coverage.run] source = ["queryapigate"]