# Python project configuration for DeepTutor # This file configures Black, Ruff, and other Python tools [build-system] requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] name = "deeptutor" version = "1.0.0" description = "An agent-native intelligent learning companion with multi-agent collaboration and RAG" requires-python = ">=3.11" readme = "README.md" license = {text = "Apache-2.0"} dependencies = [ "python-dotenv>=1.0.0", "PyYAML>=6.0", "jinja2>=3.1.0", "openai>=1.30.0", "tiktoken>=0.5.0", "aiohttp>=3.9.4", "httpx>=0.27.0", "requests>=2.32.2", "ddgs>=9.9.1", "nest_asyncio>=1.5.8", "tenacity>=8.0.0", "pydantic>=2.0.0", "pydantic-settings>=2.0.0", "aiosqlite>=0.19.0", "typer[all]>=0.9.0", "rich>=13.0.0", "prompt_toolkit>=3.0.36", ] [project.scripts] deeptutor = "deeptutor_cli.main:main" [project.optional-dependencies] anthropic = ["anthropic>=0.30.0"] dashscope = ["dashscope>=1.14.0"] search = ["perplexityai>=0.1.0"] oauth = ["oauth-cli-kit>=0.1.1; python_version >= '3.11'"] server = [ "fastapi>=0.100.0", "uvicorn[standard]>=0.24.0", "websockets>=12.0", "python-multipart>=0.0.6", ] math-animator = ["manim>=0.19.0"] all = [ "anthropic>=0.30.0", "dashscope>=1.14.0", "perplexityai>=0.1.0", "oauth-cli-kit>=0.1.1; python_version >= '3.11'", "fastapi>=0.100.0", "uvicorn[standard]>=0.24.0", "websockets>=12.0", "python-multipart>=0.0.6", "manim>=0.19.0", ] [tool.setuptools] include-package-data = true [tool.setuptools.packages.find] where = ["."] include = ["deeptutor*", "deeptutor_cli*"] # ============================================ # Black code formatter configuration # ============================================ [tool.black] line-length = 100 target-version = ['py311', 'py312'] include = '\.pyi?$' extend-exclude = ''' /( # Exclude directories \.eggs | \.git | \.hg | \.mypy_cache | \.tox | \.venv | venv | _build | buck-out | build | dist | __pycache__ | node_modules | \.next )/ ''' # ============================================ # Ruff linter and formatter configuration # ============================================ [tool.ruff] # Target Python version target-version = "py311" line-length = 100 [tool.ruff.lint] # Enable only foundational lint rules (intentionally relaxed) select = [ "E", # pycodestyle errors (语法错误) "F", # pyflakes (未使用的导入、变量等) "I", # isort (import 排序) # Keep only essential checks; other rules stay relaxed ] extend-select = [ "B006", # Do not use mutable data structures for argument defaults ] # Ignore specific rules (keep the lint profile relaxed) ignore = [ "E501", # Line too long (handled by formatter) "B008", # Do not perform function calls in argument defaults "PLR0911", # Too many return statements "PLR0912", # Too many branches "PLR0913", # Too many arguments "PLR0915", # Too many statements "PLR2004", # Magic value used in comparison "TRY003", # Avoid specifying long messages outside exception class "T201", # print found (allowed for debugging scripts) "E722", # Do not use bare except (kept relaxed for legacy paths) "E402", # Module level import not at top of file (kept relaxed) "PLC0415", # import should be at top-level (kept relaxed) "PTH123", # open() should be replaced by Path.open() (standard open allowed) "PTH103", # os.makedirs() should be replaced by Path.mkdir() (os.makedirs allowed) "PTH118", # os.path.join() should be replaced by Path (os.path.join allowed) "EM101", # Exception must not use a string literal "EM102", # Exception must not use an f-string literal "TRY002", # Create your own exception "TRY300", # Consider moving this statement to an else block "DTZ005", # datetime.now() called without tz argument "RET504", # Unnecessary assignment before return "PLW2901", # for loop variable overwritten by assignment target "F841", # Local variable assigned but never used "ERA001", # Found commented-out code (kept relaxed) ] # Exclude patterns exclude = [ ".bzr", ".direnv", ".eggs", ".git", ".git-rewrite", ".hg", ".mypy_cache", ".nox", ".pants.d", ".pyenv", ".pytest_cache", ".pytype", ".ruff_cache", ".svn", ".tox", ".venv", ".vscode", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", ".next", "out", "venv", "__pycache__", ] # Allow autofix for all enabled rules fixable = ["ALL"] unfixable = [] # Allow unused variables when underscore-prefixed dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" [tool.ruff.format] # Use double quotes for strings quote-style = "double" # Indent with spaces indent-style = "space" # Respect magic trailing comma skip-magic-trailing-comma = false # Automatically detect the appropriate line ending line-ending = "auto" # ============================================ # Ruff import sorting (isort replacement) # ============================================ [tool.ruff.lint.isort] known-first-party = ["deeptutor", "deeptutor_cli", "scripts"] force-sort-within-sections = true split-on-trailing-comma = true # ============================================ # Ruff per-file-ignores # ============================================ [tool.ruff.lint.per-file-ignores] # Allow longer lines in test files # F401: Allow unused imports for availability checks in tests "**/test_*.py" = ["E501", "PLR2004", "F401"] "**/__init__.py" = ["F401"] # Allow unused imports in __init__.py # ============================================ # Ruff mccabe complexity # ============================================ [tool.ruff.lint.mccabe] max-complexity = 10 # ============================================ # Pytest configuration # ============================================ [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["."] markers = [ "asyncio: mark async tests that require pytest-asyncio", ] asyncio_default_fixture_loop_scope = "function" addopts = [ "--strict-markers", "--strict-config", "--disable-warnings", "--tb=short" ] # ============================================ # MyPy type checking configuration # ============================================ [tool.mypy] python_version = "3.11" # Relaxed type checking for gradual adoption pretty = false show_error_context = false warn_return_any = false warn_no_return = false show_error_codes = true ignore_missing_imports = true follow_imports = "silent" # Disable strict checks that generate too many errors check_untyped_defs = false disallow_untyped_defs = false disallow_incomplete_defs = false no_implicit_optional = false # Module-specific overrides [[tool.mypy.overrides]] module = "tests.*" ignore_errors = true [[tool.mypy.overrides]] module = "deeptutor.tools.*" ignore_errors = true # Some tools may have dynamic imports # ============================================ # Bandit security linting configuration # ============================================ [tool.bandit] exclude_dirs = ["tests", "scripts"] # B101=assert, B311=random, B403/B404=pickle/subprocess imports # B110=try_except_pass (intentional for non-critical error handling) # B104=hardcoded_bind_all_interfaces (required for server binding) # B112=try_except_continue (intentional for iteration) # B105=hardcoded_password_string (false positive on empty string initialization) # B301=pickle (used for internal embedding cache, not untrusted data) # B501=request_with_no_cert_validation (opt-in via env var for dev/testing) # B603=subprocess_without_shell_equals_true (controlled execution) # B202=tarfile_unsafe_members (already using safe_members filter) skips = ["B101", "B311", "B403", "B404", "B110", "B104", "B112", "B105", "B301", "B501", "B603", "B202"]