Repository navigation
Expand file tree
/
Copy pathpyproject.toml
More file actions
179 lines (166 loc) · 6.88 KB
/
Copy pathpyproject.toml
File metadata and controls
179 lines (166 loc) · 6.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
[project]
name = "flight-cli"
version = "0.1.0"
description = "CLI for ITA Matrix's undocumented Alkali backend (flight search + lowest-fare calendar)"
readme = "README.md"
license = { file = "LICENSE" }
requires-python = ">=3.12"
authors = [{ name = "ak2k" }]
classifiers = [
"Development Status :: 3 - Alpha",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"httpx>=0.27",
"httpx-curl-cffi>=0.1.5",
"anyio>=4",
"aiolimiter>=1.1",
"stamina>=24.3",
"pydantic>=2.7",
"structlog>=24",
"typer>=0.12",
"rich>=13",
"flights>=0.9", # fli — drives Google Flights for booking handoff
"fast-flights>=2.2", # tfs= URL encoder (Google Flights deep-link)
"rookiepy>=0.5", # reads Chrome cookies for `auth pp login --from-chrome`
"diskcache>=5.6", # HTTP response cache: per-entry TTL, LRU, SQLite/WAL
]
[project.optional-dependencies]
# Patchright is a Playwright fork that patches CDP leaks + navigator.webdriver
# detection, so it passes both bot checks this project meets: Cloudflare's on the
# PointsPath login, and Google's on the `--gf-transport browser` search rung.
# The package itself is small. `patchright install chrome` is what pulls a
# browser (~150MB) and is deliberately NOT run: both paths drive the installed
# real Chrome through `channel="chrome"`.
browser = ["patchright>=1.59"]
# The login flow needs exactly what the search rung needs, so it points at that
# extra rather than repeating the pin — two copies of a version floor drift.
browser-login = ["flight-cli[browser]"]
[dependency-groups]
dev = [
"basedpyright>=1.20",
"ruff>=0.6",
"pytest>=8.3",
"pytest-cov>=5",
"hypothesis>=6.100",
# The browser fake binds its launch keywords against patchright's real
# signature, so the suite needs the package installed to RUN, not only to
# type against: without it 22 tests fail while collection still succeeds,
# because the import is function-local. `patchright install chrome` is
# still NOT run automatically and no test launches a browser — the dev
# Python package itself is small.
"patchright>=1.59",
"time-machine>=3.2",
]
[project.scripts]
flight = "flight_cli.cli:app"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/flight_cli"]
# --- ruff: lint + format ---
[tool.ruff]
target-version = "py312"
line-length = 100
src = ["src", "tests"]
[tool.ruff.lint]
select = [
"E", "W", "F", # pycodestyle + pyflakes
"I", # isort
"B", # bugbear
"UP", # pyupgrade — keeps agents from regressing to old idioms
"SIM", # simplify
"RUF", # ruff-specific
"ASYNC", # async correctness — high signal
"PTH", # pathlib over os.path
"TC", # TYPE_CHECKING import discipline
"RET", # return-statement hygiene
"PIE", # flake8-pie: misc anti-patterns
"PL", # pylint subset
"ANN", # require annotations — agents drop them otherwise
"S", # bandit (security)
"TRY", # try/except hygiene
"BLE", # no bare except
"N", # naming
]
ignore = [
"ANN401", # tactically allow Any — basedpyright reports it separately
"TRY003", # exception message length — too prescriptive
"PLR0913", # DIVERGE Profile A: CLI verb signatures are wide
"COM812", # conflicts with ruff format (trailing-comma rules)
]
# DIVERGE Profile A: D* (docstrings) intentionally omitted — small internal CLI surface.
[tool.ruff.lint.per-file-ignores]
"tests/**" = [
"S101", # asserts are pytest's primary verb
"ANN", # test signatures don't need annotations
"PLR2004", # magic numbers are fine in test data
"TRY", # try/except discipline doesn't apply
"BLE", # bare excepts are fine in tests
"N", # test function names may mirror wire-field names (camelCase)
"PLC0415", # internal lazy imports are fine for accessing private helpers
]
# DIVERGE: wire.py and pp/models.py mirror their respective upstream JSON
# shapes directly with camelCase attrs (instead of snake_case + Field(alias=...)
# on every field). Profile-B-edge: reverse-engineered, undocumented upstreams
# (Matrix, PointsPath) — the attr names ARE the contract here.
"src/flight_cli/wire.py" = ["N815"]
"src/flight_cli/pp/models.py" = ["N815"]
"src/flight_cli/providers/seats_aero/models.py" = ["N815"]
[tool.ruff.format]
docstring-code-format = true
# --- basedpyright: types ---
[tool.basedpyright]
include = ["src", "tests"]
pythonVersion = "3.12"
typeCheckingMode = "strict"
reportMissingTypeStubs = "warning"
reportImplicitOverride = "error"
reportUnusedCallResult = "none"
# DIVERGE Profile A → A/B edge: flight-cli is a CLI veneer over RE/scraping
# work. fli/fast_flights ship no stubs; Matrix's JSON response is
# dict[str, Any] by design (pydantic validates downstream); pydantic's
# ValidationInfo.data and Field overloads are Any internally. All
# reportUnknown* rules stay on (the higher-signal "can't type this"
# warnings); silencing reportAny only.
reportAny = "none"
# --- pytest ---
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra --strict-markers --strict-config"
# `tests/` on sys.path, so `from conftest import _answering` resolves by
# declaration rather than by accident. Without it the import works only under
# pytest's default `prepend` import mode, which inserts each test module's own
# basedir because `tests/` has no `__init__.py` — and `tests/pp/` and
# `tests/seats_aero/` already have one, so the arrangement is not even uniform.
pythonpath = ["tests"]
xfail_strict = true
filterwarnings = [
"error", # deprecations become test failures — catch early
"ignore::hypothesis.errors.NonInteractiveExampleWarning",
]
markers = [
# Pre-declare custom markers here; `--strict-markers` rejects undeclared ones.
# "slow: marks tests as slow (deselect with -m 'not slow')",
# "integration: requires external services",
"gf_browser: exercises the launcher seam itself (a missing patchright, the guard) and so must reach _playwright_factory; opts out of the conftest guard, and NOT a way to let a test start Chrome — a test that installs its own fake playwright has already replaced that seam and must stay unmarked",
]
# --- coverage ---
# DIVERGE Profile A: golden-file regression suite, not coverage-driven.
# Coverage machinery left in for ad-hoc inspection (`uv run pytest --cov=src`)
# but no gate — fixture parity is the real signal.
[tool.coverage.run]
source = ["src"]
branch = true
[tool.coverage.report]
exclude_also = [
"raise NotImplementedError",
"if TYPE_CHECKING:",
"if __name__ == .__main__.:",
]
show_missing = true
fail_under = 0