diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index a9aea63..ba89af3 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -34,11 +34,11 @@ jobs: pip install -e "./python[tests]" pip install flake8 mypy build pytest-cov pip-audit - name: Lint - run: cd python && python -m flake8 FlightRadar24 tests + run: cd python && python -m flake8 FlightRadarAPI FlightRadar24 tests - name: Type check - run: cd python && python -m mypy FlightRadar24 --ignore-missing-imports + run: cd python && python -m mypy FlightRadarAPI --ignore-missing-imports - name: Offline tests (PR gate) - run: cd python && pytest -m "not integration" --cov=FlightRadar24 --cov-report=term --cov-report=xml -v + run: cd python && pytest -m "not integration" --cov=FlightRadarAPI --cov-report=term --cov-report=xml -v - name: Integration tests (live FR24) if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' uses: nick-fields/retry@v3 @@ -58,4 +58,4 @@ jobs: run: | python -m build ./python pip install python/dist/*.whl --force-reinstall - python -c "from FlightRadar24 import FlightRadar24API; api = FlightRadar24API(); print('Install OK')" + python -c "from FlightRadarAPI import FlightRadar24API; api = FlightRadar24API(); print('Install OK')" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..63c6d46 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,65 @@ +# Contributing to FlightRadarAPI + +Thanks for your interest. This repo ships two SDKs in parallel — Python and +Node.js — that must stay behavior-aligned, so most non-trivial changes touch +both sides. + +## Development setup + +### Python +```bash +cd python +make dev-setup # creates venv, installs package + test extras + tooling +source venv/bin/activate +make test # runs offline + integration +make lint # flake8 +make type-check # mypy +``` + +### Node.js +```bash +cd nodejs +make install +make test # mocha (all tiers) +make lint # eslint +make test-types # tsd +``` + +## Keeping Python and Node aligned + +When you change behavior, change it in both SDKs in the same PR unless there +is a documented reason not to. Common targets that must stay in sync: + +- Error taxonomy (`AirportNotFoundError`, `LoginError`, `CloudflareError`, + `FlightRadarError`). +- `RetryPolicy` semantics (which exceptions are transient, backoff math). +- Cloudflare detection rules. +- The public surface — `FlightRadar24API` methods, the `Countries` enum, + `FlightTrackerConfig` fields, and the `Entity` / `Airport` / `Flight` + attributes consumers depend on. + +## Style + +- Python: flake8 + mypy. +- Node: eslint + tsd. +- Comments must explain **why**, not **what**. The codebase has a few exemplars in + `request.py`/`request.js` — read those before adding new comments. + +## Commits and PRs + +- Use a descriptive title with a conventional-commits prefix (`fix:`, `feat:`, + `refactor:`, `docs:`, `ci:`, `test:`). +- For new endpoints or behavior tweaks, add a regression test alongside. + +## Releases + +Before publishing a new release, the version **must be bumped**. The version lives in two places: + +- `python/FlightRadarAPI/__init__.py` (`__version__`) +- `nodejs/package.json` (`version`) + +## Reporting bugs and asking questions + +- Bugs: open a GitHub issue with the bug report template. +- Security: see [`SECURITY.md`](SECURITY.md). **Do not** report via GitHub + issues. diff --git a/README.md b/README.md index bb11631..887612c 100644 --- a/README.md +++ b/README.md @@ -25,4 +25,9 @@ npm install flightradarapi ``` ## Documentation -Explore the documentation of FlightRadarAPI package, for Python or NodeJS, through [this site](https://JeanExtreme002.github.io/FlightRadarAPI/). +Explore the docs of FlightRadarAPI package, for Python or NodeJS, through [FlightRadarAPI Documentation](https://JeanExtreme002.github.io/FlightRadarAPI/) page. + +## Project resources +**Contributing**: [`CONTRIBUTING.md`](CONTRIBUTING.md)
+**Security policy**: [`SECURITY.md`](SECURITY.md)
+ diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..9dc913c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,38 @@ +# Security Policy + +## Supported Versions + +Security fixes are applied to the latest minor release on PyPI and npm. Older +versions do not receive backports. + +## Reporting a Vulnerability + +**Do not open a public GitHub issue for security reports.** + +Please email the maintainer with: + +- A description of the issue and the affected component (Python SDK, Node SDK, + CI, or documentation). +- The minimum reproducer you have (a short script is ideal). +- The package version (`pip show FlightRadarAPI` / `npm ls flightradarapi`). +- The impact you believe the issue has. + +## Scope + +This project is an **unofficial SDK** that consumes endpoints from +flightradar24. The following are explicitly **out of scope** for security +reports: + +- The FR24 site or upstream API itself — contact FR24 directly. +- TLS impersonation behavior (intentional; see `request.py` / `request.js`). +- The fact that the SDK can be blocked by Cloudflare under heavy use. +- Anything that requires the user to feed the SDK adversarial input *to their + own credentials or filesystem*. + +## In Scope + +- Code execution, injection, or filesystem traversal triggered by a normal + call surface. +- Credential leakage in logs, error messages, or exception payloads. +- Known-vulnerable transitive dependencies that the SDK actually exercises. +- Improper validation that turns a remote response into local code paths. diff --git a/docs/python.md b/docs/python.md index f345002..e7a3fc7 100644 --- a/docs/python.md +++ b/docs/python.md @@ -16,7 +16,7 @@ pip install FlightRadarAPI Start by importing the `FlightRadar24API` class and creating an instance of it: ```python -from FlightRadar24 import FlightRadar24API +from FlightRadarAPI import FlightRadar24API fr_api = FlightRadar24API() ``` diff --git a/nodejs/FlightRadar24/api.js b/nodejs/FlightRadarAPI/api.js similarity index 91% rename from nodejs/FlightRadar24/api.js rename to nodejs/FlightRadarAPI/api.js index d1676d2..3a27623 100644 --- a/nodejs/FlightRadar24/api.js +++ b/nodejs/FlightRadarAPI/api.js @@ -108,7 +108,7 @@ class FlightRadar24API { */ async getAirport(code, details = false) { if (code.length < 3 || code.length > 4) { - throw new Error("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport."); + throw new TypeError("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport."); } if (details) { @@ -138,7 +138,7 @@ class FlightRadar24API { */ async getAirportDetails(code, flightLimit = 100, page = 1) { if (code.length < 3 || code.length > 4) { - throw new Error("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport."); + throw new TypeError("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport."); } const params = { "format": "json", "code": code, "limit": flightLimit, "page": page }; @@ -159,7 +159,7 @@ class FlightRadar24API { const limit = errors?.["limit"]; if (limit !== undefined) { - throw new Error(limit["notBetween"]); + throw new RangeError(limit["notBetween"]); } throw new AirportNotFoundError("Could not find an airport by the code '" + code + "'.", errors); } @@ -198,9 +198,13 @@ class FlightRadar24API { */ async getAirports(countries) { const airports = []; + // Use stateless requests for the fan-out so per-country `Set-Cookie` + // responses do not race onto the shared session jar. await mapConcurrent(countries, this.maxWorkers, async (countryName) => { const countryHref = Core.airportsDataUrl + "/" + countryName; - const { content } = await this.__client.request(countryHref, { headers: Core.htmlHeaders, timeout: this.timeout }); + const { content } = await this.__client.requestStandalone( + countryHref, { headers: Core.htmlHeaders, timeout: this.timeout }, + ); airports.push(...parseAirportsHtml(content, countryHref)); }); return airports; @@ -250,30 +254,38 @@ class FlightRadar24API { const lon = radians(longitude); const approxEarthRadius = 6371; + + // Distance from the centre to a corner of the bounding square. const hypotenuseDistance = Math.sqrt(2 * (Math.pow(halfSideInKm, 2))); + // Diagonal bearings: 225° (SW corner, min lat/lon) and 45° (NE corner, max lat/lon). + const bearingSw = radians(225); + const bearingNe = radians(45); + + // Destination-point formula along the SW bearing → south-west corner. const latMin = Math.asin( Math.sin(lat) * Math.cos(hypotenuseDistance / approxEarthRadius) + Math.cos(lat) * Math.sin(hypotenuseDistance / approxEarthRadius) * - Math.cos(225 * (Math.PI / 180)), + Math.cos(bearingSw), ); const lonMin = lon + Math.atan2( - Math.sin(225 * (Math.PI / 180)) * + Math.sin(bearingSw) * Math.sin(hypotenuseDistance / approxEarthRadius) * Math.cos(lat), Math.cos(hypotenuseDistance / approxEarthRadius) - Math.sin(lat) * Math.sin(latMin), ); + // Same formula along the NE bearing → north-east corner. const latMax = Math.asin( Math.sin(lat) * Math.cos(hypotenuseDistance / approxEarthRadius) + Math.cos(lat) * Math.sin(hypotenuseDistance / approxEarthRadius) * - Math.cos(45 * (Math.PI / 180)), + Math.cos(bearingNe), ); const lonMax = lon + Math.atan2( - Math.sin(45 * (Math.PI / 180)) * + Math.sin(bearingNe) * Math.sin(hypotenuseDistance / approxEarthRadius) * Math.cos(lat), Math.cos(hypotenuseDistance / approxEarthRadius) - @@ -296,7 +308,8 @@ class FlightRadar24API { * @return {Promise<[object, string] | null>} */ async getCountryFlag(country) { - const flagUrl = Core.countryFlagUrl(country.toLowerCase().replaceAll(" ", "-")); + const slug = country.toLowerCase().replaceAll(" ", "-"); + const flagUrl = Core.countryFlagUrl(slug); const headers = { ...Core.imageHeaders }; delete headers["origin"]; @@ -321,7 +334,9 @@ class FlightRadar24API { * @return {Promise} */ async getFlightDetails(flight) { - const { content } = await this.__client.request( + // Stateless request so the concurrent fan-out in `getFlights(..., details=true)` + // doesn't interleave cookie writes on the shared session. + const { content } = await this.__client.requestStandalone( Core.flightDataUrl(flight.id), { headers: Core.jsonHeaders, timeout: this.timeout }, ); return content; @@ -399,7 +414,7 @@ class FlightRadar24API { fileType = fileType.toLowerCase(); if (!["csv", "kml"].includes(fileType)) { - throw new Error("File type '" + fileType + "' is not supported. Only CSV and KML are supported."); + throw new TypeError("File type '" + fileType + "' is not supported. Only CSV and KML are supported."); } const headers = { ...Core.jsonHeaders, "accesstoken": this.getLoginData()["accessToken"] }; diff --git a/nodejs/FlightRadar24/core.js b/nodejs/FlightRadarAPI/core.js similarity index 100% rename from nodejs/FlightRadar24/core.js rename to nodejs/FlightRadarAPI/core.js diff --git a/nodejs/FlightRadar24/entities/airport.js b/nodejs/FlightRadarAPI/entities/airport.js similarity index 100% rename from nodejs/FlightRadar24/entities/airport.js rename to nodejs/FlightRadarAPI/entities/airport.js diff --git a/nodejs/FlightRadar24/entities/entity.js b/nodejs/FlightRadarAPI/entities/entity.js similarity index 94% rename from nodejs/FlightRadar24/entities/entity.js rename to nodejs/FlightRadarAPI/entities/entity.js index ecf4709..91cd0ca 100644 --- a/nodejs/FlightRadar24/entities/entity.js +++ b/nodejs/FlightRadarAPI/entities/entity.js @@ -45,7 +45,7 @@ class Entity { getDistanceFrom(entity) { if (this.latitude == null || this.longitude == null || entity.latitude == null || entity.longitude == null) { - throw new Error("Cannot calculate distance: one or both entities have no position."); + throw new TypeError("Cannot calculate distance: one or both entities have no position."); } const lat1 = radians(this.latitude); diff --git a/nodejs/FlightRadar24/entities/flight.js b/nodejs/FlightRadarAPI/entities/flight.js similarity index 100% rename from nodejs/FlightRadar24/entities/flight.js rename to nodejs/FlightRadarAPI/entities/flight.js diff --git a/nodejs/FlightRadar24/errors.js b/nodejs/FlightRadarAPI/errors.js similarity index 100% rename from nodejs/FlightRadar24/errors.js rename to nodejs/FlightRadarAPI/errors.js diff --git a/nodejs/FlightRadar24/flightTrackerConfig.js b/nodejs/FlightRadarAPI/flightTrackerConfig.js similarity index 88% rename from nodejs/FlightRadar24/flightTrackerConfig.js rename to nodejs/FlightRadarAPI/flightTrackerConfig.js index cbcb7fb..d7640da 100644 --- a/nodejs/FlightRadar24/flightTrackerConfig.js +++ b/nodejs/FlightRadarAPI/flightTrackerConfig.js @@ -4,10 +4,10 @@ const { isNumeric } = require("./util"); const proxyHandler = { set: function(target, key, value) { if (!Object.prototype.hasOwnProperty.call(target, key)) { - throw new Error("Unknown option: '" + key + "'"); + throw new RangeError("Unknown option: '" + key + "'"); } if ((typeof value !== "number") && (!isNumeric(value))) { - throw new Error("Value must be a number. Got '" + value + "' for key '" + key + "'"); + throw new TypeError("Value must be a number. Got '" + value + "' for key '" + key + "'"); } target[key] = value.toString(); return true; diff --git a/nodejs/FlightRadar24/index.d.ts b/nodejs/FlightRadarAPI/index.d.ts similarity index 98% rename from nodejs/FlightRadar24/index.d.ts rename to nodejs/FlightRadarAPI/index.d.ts index 5fad6d6..42b6273 100644 --- a/nodejs/FlightRadar24/index.d.ts +++ b/nodejs/FlightRadarAPI/index.d.ts @@ -26,6 +26,11 @@ export interface ImpersonateOptions { export class APIClient { constructor(options?: { impersonate?: ImpersonateOptions; retry?: RetryPolicy }); request(url: string, options?: object): Promise<{content: any; statusCode: number; cookies: Record}>; + /** + * Make a stateless request that bypasses the shared cookie jar. Safe to + * call from concurrent fan-outs (e.g. `getAirports`). + */ + requestStandalone(url: string, options?: object): Promise<{content: any; statusCode: number; cookies: Record}>; getCookie(name: string): string | undefined; clearCookies(): void; } diff --git a/nodejs/FlightRadar24/index.js b/nodejs/FlightRadarAPI/index.js similarity index 100% rename from nodejs/FlightRadar24/index.js rename to nodejs/FlightRadarAPI/index.js diff --git a/nodejs/FlightRadar24/parsers.js b/nodejs/FlightRadarAPI/parsers.js similarity index 100% rename from nodejs/FlightRadar24/parsers.js rename to nodejs/FlightRadarAPI/parsers.js diff --git a/nodejs/FlightRadar24/request.js b/nodejs/FlightRadarAPI/request.js similarity index 93% rename from nodejs/FlightRadar24/request.js rename to nodejs/FlightRadarAPI/request.js index 4375072..7cda54a 100644 --- a/nodejs/FlightRadar24/request.js +++ b/nodejs/FlightRadarAPI/request.js @@ -321,7 +321,7 @@ class Session { } /** - * Central HTTP client for the FlightRadar24 package. + * Central HTTP client for the FlightRadarAPI package. * * Owns the persistent session (cookie jar, TLS fingerprint, future bypass logic) * so that the rest of the codebase never has to deal with those concerns directly. @@ -333,8 +333,8 @@ class APIClient { * (`{ciphers, sigalgs, ecdhCurve}`). Falls back to the bundled Chrome 136 profile. */ constructor({ impersonate = null, retry = null } = {}) { - const dispatcher = impersonate ? buildImpersonateAgent(impersonate) : defaultAgent; - this.__session = new Session({ dispatcher }); + this.__dispatcher = impersonate ? buildImpersonateAgent(impersonate) : defaultAgent; + this.__session = new Session({ dispatcher: this.__dispatcher }); this.__retry = retry; } @@ -349,6 +349,24 @@ class APIClient { return runWithRetry(() => this.__session.request(url, options), this.__retry); } + /** + * Make a stateless request that does not touch the shared cookie jar. + * + * Safe to call from concurrent fan-outs (e.g. `getAirports` issuing one + * request per country). The TLS dispatcher is still reused so the + * impersonation profile stays consistent with the session. + * + * @param {string} url + * @param {object} [options={}] + * @return {Promise<{content: *, statusCode: number, cookies: object}>} + */ + async requestStandalone(url, options = {}) { + return runWithRetry( + () => request(url, { dispatcher: this.__dispatcher, ...options }), + this.__retry, + ); + } + /** * Return the value of a stored cookie by name. * diff --git a/nodejs/FlightRadar24/util.js b/nodejs/FlightRadarAPI/util.js similarity index 100% rename from nodejs/FlightRadar24/util.js rename to nodejs/FlightRadarAPI/util.js diff --git a/nodejs/FlightRadar24/zones.js b/nodejs/FlightRadarAPI/zones.js similarity index 100% rename from nodejs/FlightRadar24/zones.js rename to nodejs/FlightRadarAPI/zones.js diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 9890a6d..94905d4 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -1,12 +1,12 @@ { "name": "flightradarapi", - "version": "1.5.0", + "version": "1.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "flightradarapi", - "version": "1.5.0", + "version": "1.5.1", "license": "MIT", "dependencies": { "node-html-parser": "^6.1.13", diff --git a/nodejs/package.json b/nodejs/package.json index 409e9bd..e9cd251 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -1,9 +1,9 @@ { "name": "flightradarapi", - "version": "1.5.0", + "version": "1.5.1", "description": "SDK for FlightRadar24", - "main": "./FlightRadar24/index.js", - "types": "./FlightRadar24/index.d.ts", + "main": "./FlightRadarAPI/index.js", + "types": "./FlightRadarAPI/index.d.ts", "scripts": { "test": "mocha tests --timeout 10000", "test:offline": "mocha tests/testParsersOffline.js tests/testRequestPolicy.js tests/testRequestTransport.js --timeout 10000", diff --git a/nodejs/tests/index.test-d.ts b/nodejs/tests/index.test-d.ts index eeece41..c5aaaa1 100644 --- a/nodejs/tests/index.test-d.ts +++ b/nodejs/tests/index.test-d.ts @@ -5,7 +5,7 @@ import { Airport, FlightTrackerConfig, Zone, -} from "../FlightRadar24/index"; +} from "../FlightRadarAPI/index"; const api = new FlightRadar24API(); expectType(new FlightRadar24API({timeout: 5000, maxWorkers: 4})); diff --git a/nodejs/tests/testParsersOffline.js b/nodejs/tests/testParsersOffline.js index 7ef6c91..2e71b15 100644 --- a/nodejs/tests/testParsersOffline.js +++ b/nodejs/tests/testParsersOffline.js @@ -11,7 +11,7 @@ const fs = require("fs"); const path = require("path"); const expect = require("chai").expect; -const { parseAirlinesHtml, parseAirportsHtml } = require("../FlightRadar24/parsers"); +const { parseAirlinesHtml, parseAirportsHtml } = require("../FlightRadarAPI/parsers"); const FIXTURES = path.join(__dirname, "fixtures"); const load = (name) => fs.readFileSync(path.join(FIXTURES, name), "utf-8"); diff --git a/nodejs/tests/testRequestPolicy.js b/nodejs/tests/testRequestPolicy.js index f6d44f0..21c5ee8 100644 --- a/nodejs/tests/testRequestPolicy.js +++ b/nodejs/tests/testRequestPolicy.js @@ -16,8 +16,8 @@ const { expect } = require("chai"); const { MockAgent } = require("undici"); -const { request, RetryPolicy, APIClient } = require("../FlightRadar24/request"); -const { CloudflareError } = require("../FlightRadar24/errors"); +const { request, RetryPolicy, APIClient } = require("../FlightRadarAPI/request"); +const { CloudflareError } = require("../FlightRadarAPI/errors"); const { clientWithFakeSession } = require("./_requestDoubles"); diff --git a/nodejs/tests/testRequestTransport.js b/nodejs/tests/testRequestTransport.js index 9af643b..bda12c4 100644 --- a/nodejs/tests/testRequestTransport.js +++ b/nodejs/tests/testRequestTransport.js @@ -18,7 +18,7 @@ const { expect } = require("chai"); const { MockAgent } = require("undici"); -const { request } = require("../FlightRadar24/request"); +const { request } = require("../FlightRadarAPI/request"); describe("Content-type dispatch (transport)", function() { diff --git a/python/.flake8 b/python/.flake8 index 704b941..6cbc5eb 100644 --- a/python/.flake8 +++ b/python/.flake8 @@ -11,4 +11,4 @@ per-file-ignores = tests/package.py:F401, E402 # zones.py is a data file with 2-space indentation; E121 (hanging indent) does not apply. - FlightRadar24/zones.py:E121 \ No newline at end of file + FlightRadarAPI/zones.py:E121 \ No newline at end of file diff --git a/python/FlightRadar24/__init__.py b/python/FlightRadar24/__init__.py index 66bfa6d..ccf748f 100644 --- a/python/FlightRadar24/__init__.py +++ b/python/FlightRadar24/__init__.py @@ -1,36 +1,37 @@ # -*- coding: utf-8 -*- """ -Unofficial SDK for FlightRadar24. +Deprecated import alias for the FlightRadarAPI SDK. -This SDK provides flight and airport data available to the public -on the FlightRadar24 website. - -See more information at: -https://www.flightradar24.com/premium/ -https://www.flightradar24.com/terms-and-conditions +The package was renamed to ``FlightRadarAPI`` so the Python import name +matches the PyPI distribution name and the Node.js package name. This +module re-exports the public API and aliases every submodule so legacy +imports such as ``from FlightRadar24 import FlightRadar24API`` or +``from FlightRadar24.errors import CloudflareError`` keep working, but a +``DeprecationWarning`` is emitted on import. """ -__author__ = "Jean Loui Bernard Silva de Jesus" -__version__ = "1.5.0" +import importlib +import pkgutil +import sys +import warnings + +import FlightRadarAPI as _pkg + +warnings.warn( + "Importing from 'FlightRadar24' is deprecated and will be removed in a " + "future release. Import from 'FlightRadarAPI' instead " + "(e.g. 'from FlightRadarAPI import FlightRadar24API').", + DeprecationWarning, + stacklevel=2, +) -from .api import FlightRadar24API -from .core import Countries -from .entities import Airport, Entity, Flight -from .errors import AirportNotFoundError, CloudflareError, FlightRadarError, LoginError -from .flight_tracker_config import FlightTrackerConfig -from .request import RetryPolicy +# Mirror every submodule of FlightRadarAPI under the legacy FlightRadar24 +# namespace so dotted imports keep resolving without touching disk. +for _info in pkgutil.walk_packages(_pkg.__path__, prefix=f"{_pkg.__name__}."): + _mod = importlib.import_module(_info.name) + sys.modules[_info.name.replace(_pkg.__name__, __name__, 1)] = _mod +del _info, _mod -__all__ = [ - "FlightRadar24API", - "Countries", - "Airport", - "Entity", - "Flight", - "AirportNotFoundError", - "CloudflareError", - "FlightRadarError", - "LoginError", - "FlightTrackerConfig", - "RetryPolicy", -] +from FlightRadarAPI import * # noqa: E402, F401, F403 +from FlightRadarAPI import __all__, __author__, __version__ # noqa: E402, F401 diff --git a/python/FlightRadarAPI/__init__.py b/python/FlightRadarAPI/__init__.py new file mode 100644 index 0000000..4c60769 --- /dev/null +++ b/python/FlightRadarAPI/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +""" +Unofficial SDK for FlightRadar24. + +This SDK provides flight and airport data available to the public +on the FlightRadar24 website. + +See more information at: +https://www.flightradar24.com/premium/ +https://www.flightradar24.com/terms-and-conditions +""" + +__author__ = "Jean Loui Bernard Silva de Jesus" +__version__ = "1.5.1" + +from .api import FlightRadar24API +from .core import Countries +from .entities import Airport, Entity, Flight +from .errors import AirportNotFoundError, CloudflareError, FlightRadarError, LoginError +from .flight_tracker_config import FlightTrackerConfig +from .request import RetryPolicy + +__all__ = [ + "FlightRadar24API", + "Countries", + "Airport", + "Entity", + "Flight", + "AirportNotFoundError", + "CloudflareError", + "FlightRadarError", + "LoginError", + "FlightTrackerConfig", + "RetryPolicy", +] diff --git a/python/FlightRadar24/api.py b/python/FlightRadarAPI/api.py similarity index 95% rename from python/FlightRadar24/api.py rename to python/FlightRadarAPI/api.py index c06feed..41ab442 100644 --- a/python/FlightRadar24/api.py +++ b/python/FlightRadarAPI/api.py @@ -38,7 +38,7 @@ def __init__( :param max_workers: Maximum threads used when fetching flight details concurrently :param impersonate: TLS impersonation profile (curl_cffi). Override when FR24 updates its Cloudflare bot mitigation faster than this library releases. - See ``FlightRadar24.request.DEFAULT_IMPERSONATE`` for the current default. + See ``FlightRadarAPI.request.DEFAULT_IMPERSONATE`` for the current default. :param retry: Optional :class:`RetryPolicy` applied to transient failures (``CloudflareError`` and curl_cffi network errors). Defaults to no retry. """ @@ -230,30 +230,39 @@ def get_bounds_by_point(self, latitude: float, longitude: float, radius: float) lon = math.radians(longitude) approx_earth_radius = 6371 + + # Distance from the centre to a corner of the bounding square. hypotenuse_distance = math.sqrt(2 * (math.pow(half_side_in_km, 2))) + # The two diagonal bearings (in radians) used below are 225° (SW corner, + # yields the min lat/lon) and 45° (NE corner, yields the max lat/lon). + bearing_sw = math.radians(225) + bearing_ne = math.radians(45) + + # Destination-point formula along the SW bearing → south-west corner. lat_min = math.asin( math.sin(lat) * math.cos(hypotenuse_distance / approx_earth_radius) + math.cos(lat) * math.sin(hypotenuse_distance / approx_earth_radius) - * math.cos(225 * (math.pi / 180)), + * math.cos(bearing_sw), ) lon_min = lon + math.atan2( - math.sin(225 * (math.pi / 180)) + math.sin(bearing_sw) * math.sin(hypotenuse_distance / approx_earth_radius) * math.cos(lat), math.cos(hypotenuse_distance / approx_earth_radius) - math.sin(lat) * math.sin(lat_min), ) + # Same formula along the NE bearing → north-east corner. lat_max = math.asin( math.sin(lat) * math.cos(hypotenuse_distance / approx_earth_radius) + math.cos(lat) * math.sin(hypotenuse_distance / approx_earth_radius) - * math.cos(45 * (math.pi / 180)), + * math.cos(bearing_ne), ) lon_max = lon + math.atan2( - math.sin(45 * (math.pi / 180)) + math.sin(bearing_ne) * math.sin(hypotenuse_distance / approx_earth_radius) * math.cos(lat), math.cos(hypotenuse_distance / approx_earth_radius) @@ -276,7 +285,8 @@ def get_country_flag(self, country: str) -> Optional[Tuple[bytes, str]]: :param country: Country name """ - flag_url = Core.country_flag_url.format(country.lower().replace(" ", "-")) + slug = country.lower().replace(" ", "-") + flag_url = Core.country_flag_url.format(slug) headers = Core.image_headers.copy() headers.pop("origin", None) # Does not work for this request. diff --git a/python/FlightRadar24/core.py b/python/FlightRadarAPI/core.py similarity index 100% rename from python/FlightRadar24/core.py rename to python/FlightRadarAPI/core.py diff --git a/python/FlightRadar24/entities/__init__.py b/python/FlightRadarAPI/entities/__init__.py similarity index 100% rename from python/FlightRadar24/entities/__init__.py rename to python/FlightRadarAPI/entities/__init__.py diff --git a/python/FlightRadar24/entities/airport.py b/python/FlightRadarAPI/entities/airport.py similarity index 100% rename from python/FlightRadar24/entities/airport.py rename to python/FlightRadarAPI/entities/airport.py diff --git a/python/FlightRadar24/entities/entity.py b/python/FlightRadarAPI/entities/entity.py similarity index 100% rename from python/FlightRadar24/entities/entity.py rename to python/FlightRadarAPI/entities/entity.py diff --git a/python/FlightRadar24/entities/flight.py b/python/FlightRadarAPI/entities/flight.py similarity index 100% rename from python/FlightRadar24/entities/flight.py rename to python/FlightRadarAPI/entities/flight.py diff --git a/python/FlightRadar24/errors.py b/python/FlightRadarAPI/errors.py similarity index 100% rename from python/FlightRadar24/errors.py rename to python/FlightRadarAPI/errors.py diff --git a/python/FlightRadar24/flight_tracker_config.py b/python/FlightRadarAPI/flight_tracker_config.py similarity index 100% rename from python/FlightRadar24/flight_tracker_config.py rename to python/FlightRadarAPI/flight_tracker_config.py diff --git a/python/FlightRadar24/parsers.py b/python/FlightRadarAPI/parsers.py similarity index 100% rename from python/FlightRadar24/parsers.py rename to python/FlightRadarAPI/parsers.py diff --git a/python/FlightRadar24/py.typed b/python/FlightRadarAPI/py.typed similarity index 100% rename from python/FlightRadar24/py.typed rename to python/FlightRadarAPI/py.typed diff --git a/python/FlightRadar24/request.py b/python/FlightRadarAPI/request.py similarity index 99% rename from python/FlightRadar24/request.py rename to python/FlightRadarAPI/request.py index a44d74b..06fee97 100644 --- a/python/FlightRadar24/request.py +++ b/python/FlightRadarAPI/request.py @@ -76,7 +76,7 @@ def _run_with_retry(fn, retry: Optional[RetryPolicy]): class APIClient: """ - Central HTTP client for the FlightRadar24 package. + Central HTTP client for the FlightRadarAPI package. Owns the persistent session (cookie jar, TLS fingerprint, future bypass logic) so that the rest of the codebase never has to deal with those concerns directly. diff --git a/python/FlightRadar24/zones.py b/python/FlightRadarAPI/zones.py similarity index 100% rename from python/FlightRadar24/zones.py rename to python/FlightRadarAPI/zones.py diff --git a/python/Makefile b/python/Makefile index 093d9d6..6e93445 100644 --- a/python/Makefile +++ b/python/Makefile @@ -1,20 +1,41 @@ # Makefile for FlightRadarAPI Python package # Variables -PACKAGE_NAME = FlightRadar24 -PYTHON = python3 -PIP = pip3 +PACKAGE_NAME = FlightRadarAPI BUILD_DIR = build DIST_DIR = dist EGG_INFO = $(PACKAGE_NAME).egg-info VENV_DIR = venv TEST_DIR = tests +# Minimum offline coverage required by `test-coverage` and by the CI workflow. +# Keep this single source-of-truth in sync with `.github/workflows/python-package.yml` +# (raise both together when the offline suite grows). +COVERAGE_MIN = 60 + +# Dev tooling installed by `install-dev` / `dev-setup` on top of `.[tests]`. +# Centralised so the two targets do not drift. +DEV_TOOLS = pytest-cov flake8 black mypy twine build hatch pip-audit + +# Auto-detect virtualenv. When `venv/` already exists, every target uses its +# interpreter so `make test` / `make lint` / etc. work without first running +# `source venv/bin/activate`. Otherwise we fall back to the system python. +# +# Targets that *create* the venv (notably `dev-setup`) must call +# `$(VENV_DIR)/bin/pip` directly — variable expansion happens at parse time, +# so the auto-detection below cannot pick up a venv created later in the +# same `make` invocation. +ifneq ($(wildcard $(VENV_DIR)/bin/python),) +PYTHON := $(VENV_DIR)/bin/python +PIP := $(VENV_DIR)/bin/pip +else +PYTHON := python3 +PIP := pip3 +endif + # Colors for output GREEN = \033[0;32m YELLOW = \033[0;33m -RED = \033[0;31m -BLUE = \033[0;34m NC = \033[0m # No Color # Default target @@ -43,47 +64,49 @@ help: @echo " $(YELLOW)check-deps$(NC) - Check for outdated dependencies" @echo " $(YELLOW)update-deps$(NC) - Update dependencies" @echo " $(YELLOW)security$(NC) - Run security audit" - @echo " $(YELLOW)docs$(NC) - Generate documentation" + @echo " $(YELLOW)docs$(NC) - Build the MkDocs site (output in ../site/)" + @echo " $(YELLOW)docs-serve$(NC) - Serve the MkDocs site locally with auto-reload" @echo " $(YELLOW)venv$(NC) - Create virtual environment" - @echo " $(YELLOW)venv-activate$(NC) - Show command to activate venv" @echo " $(YELLOW)all$(NC) - Run full pipeline (install, lint, test, build)" -# Create virtual environment +# Create virtual environment. +# +# Uses `python3` literally rather than `$(PYTHON)`: when this target runs with +# an existing venv on disk the auto-detection at the top would point at +# `venv/bin/python` and we'd be asking the venv to recreate itself, which is +# both confusing and unreliable on Python builds with restricted `venv` +# support. The system python3 is the right bootstrap interpreter. .PHONY: venv venv: @echo "$(GREEN)Creating virtual environment...$(NC)" - $(PYTHON) -m venv $(VENV_DIR) + python3 -m venv $(VENV_DIR) @echo "$(GREEN)Virtual environment created in $(VENV_DIR)$(NC)" @echo "$(YELLOW)To activate: source $(VENV_DIR)/bin/activate$(NC)" -# Show activation command -.PHONY: venv-activate -venv-activate: - @echo "$(YELLOW)To activate virtual environment run:$(NC)" - @echo "source $(VENV_DIR)/bin/activate" +# Install package in editable mode (no dev tools). +.PHONY: install +install: + @echo "$(GREEN)Installing package in editable mode...$(NC)" + $(PIP) install -e . + @echo "$(GREEN)Package installed successfully!$(NC)" -# Install dependencies +# Backwards-compatible alias for `install`. Kept so older docs / muscle memory +# referencing `make install-deps` keep working; consolidating eliminated the +# old redundant double `pip install -e .` between `install-deps` and `install`. .PHONY: install-deps -install-deps: - @echo "$(GREEN)Installing dependencies...$(NC)" - $(PIP) install -e . - @echo "$(GREEN)Dependencies installed successfully!$(NC)" +install-deps: install -# Install development dependencies +# Install everything needed for development: package (editable) + test extras +# + the tooling used by other Makefile targets (lint, type-check, security, +# build). Dev tools are defined once in `$(DEV_TOOLS)` so this target and +# `dev-setup` cannot drift. .PHONY: install-dev install-dev: @echo "$(GREEN)Installing development dependencies...$(NC)" $(PIP) install -e ".[tests]" - $(PIP) install pytest-cov flake8 black mypy twine build hatch + $(PIP) install $(DEV_TOOLS) @echo "$(GREEN)Development dependencies installed successfully!$(NC)" -# Install package in development mode -.PHONY: install -install: install-deps - @echo "$(GREEN)Installing package in development mode...$(NC)" - $(PIP) install -e . - @echo "$(GREEN)Package installed successfully!$(NC)" - # Run tests .PHONY: test test: @@ -98,11 +121,12 @@ test-verbose: $(PYTHON) -m pytest $(TEST_DIR) -v -s @echo "$(GREEN)Verbose tests completed!$(NC)" -# Run tests with coverage +# Run tests with coverage. Threshold lives in $(COVERAGE_MIN) so it stays in +# sync with the CI workflow — bump both together. .PHONY: test-coverage test-coverage: - @echo "$(GREEN)Running tests with coverage...$(NC)" - $(PYTHON) -m pytest $(TEST_DIR) --cov=$(PACKAGE_NAME) --cov-report=html --cov-report=term + @echo "$(GREEN)Running tests with coverage (min $(COVERAGE_MIN)%)...$(NC)" + $(PYTHON) -m pytest $(TEST_DIR) --cov=$(PACKAGE_NAME) --cov-report=html --cov-report=term --cov-fail-under=$(COVERAGE_MIN) @echo "$(GREEN)Coverage report generated!$(NC)" @echo "$(YELLOW)HTML report available at htmlcov/index.html$(NC)" @@ -127,41 +151,43 @@ type-check: $(PYTHON) -m mypy $(PACKAGE_NAME) --ignore-missing-imports @echo "$(GREEN)Type checking completed!$(NC)" -# Clean build artifacts -.PHONY: clean -clean: +# Clean *build* artifacts only. Kept narrow so that `build` (and everything +# that depends on it: `validate`, `publish`, `release`) does not silently +# wipe coverage reports or pytest/mypy caches that the developer just ran. +.PHONY: clean-build +clean-build: @echo "$(GREEN)Cleaning build artifacts...$(NC)" - rm -rf $(BUILD_DIR) - rm -rf $(DIST_DIR) - rm -rf $(EGG_INFO) - rm -rf .pytest_cache - rm -rf htmlcov - rm -rf .coverage - rm -rf .mypy_cache + rm -rf $(BUILD_DIR) $(DIST_DIR) $(EGG_INFO) + +# Full clean: build artifacts + caches + coverage reports. Use this between +# branches or before publishing. Note that `build` no longer depends on +# `clean` — call `make clean build` if you want a fully scrubbed build. +.PHONY: clean +clean: clean-build + @echo "$(GREEN)Cleaning caches and coverage reports...$(NC)" + rm -rf .pytest_cache htmlcov .coverage .mypy_cache find . -type d -name "__pycache__" -exec rm -rf {} + - find . -type f -name "*.pyc" -delete - find . -type f -name "*.pyo" -delete - find . -type f -name "*.pyd" -delete - find . -type f -name ".coverage" -delete + find . -type f \( -name "*.pyc" -o -name "*.pyo" -o -name "*.pyd" \) -delete @echo "$(GREEN)Cleanup completed!$(NC)" -# Build package +# Build package. Depends on `clean-build` (not `clean`) so coverage / cache +# files survive — see the comment on `clean-build` for motivation. .PHONY: build -build: clean +build: clean-build @echo "$(GREEN)Building package...$(NC)" $(PYTHON) -m build @echo "$(GREEN)Package built successfully!$(NC)" # Build wheel package only .PHONY: build-wheel -build-wheel: clean +build-wheel: clean-build @echo "$(GREEN)Building wheel package...$(NC)" $(PYTHON) -m build --wheel @echo "$(GREEN)Wheel package built successfully!$(NC)" # Build source distribution only .PHONY: build-sdist -build-sdist: clean +build-sdist: clean-build @echo "$(GREEN)Building source distribution...$(NC)" $(PYTHON) -m build --sdist @echo "$(GREEN)Source distribution built successfully!$(NC)" @@ -200,43 +226,65 @@ check-deps: @echo "$(GREEN)Checking for outdated dependencies...$(NC)" $(PIP) list --outdated -# Update dependencies +# Update every dependency that `install-dev` installs — the package itself +# (editable, with test extras) AND the dev tools. Updating only the extras +# (the previous behaviour) silently drifted flake8/mypy/etc. behind. .PHONY: update-deps update-deps: @echo "$(GREEN)Updating dependencies...$(NC)" $(PIP) install --upgrade -e ".[tests]" + $(PIP) install --upgrade $(DEV_TOOLS) @echo "$(GREEN)Dependencies updated!$(NC)" -# Security audit +# Security audit. Uses `pip-audit` to match the CI workflow (the older +# `safety` CLI was retired upstream and is no longer the recommended tool). +# Assumes `make install-dev` already installed `pip-audit`. .PHONY: security security: - @echo "$(GREEN)Running security audit...$(NC)" - $(PIP) install safety - safety check + @echo "$(GREEN)Running security audit (pip-audit)...$(NC)" + $(PYTHON) -m pip_audit @echo "$(GREEN)Security audit completed!$(NC)" -# Generate documentation +# Build the MkDocs site. The configuration (`mkdocs.yml`) and the `docs/` +# directory live in the repository root, so we invoke MkDocs from one level +# up. Requires `mkdocs-material` (install with `pip install mkdocs-material` +# — kept out of `install-dev` because the docs site is rebuilt by CI, not +# every developer needs the dependency locally). .PHONY: docs docs: - @echo "$(GREEN)Generating documentation...$(NC)" - @if [ -d "docs" ]; then \ - cd docs && make html; \ - echo "$(GREEN)Documentation generated in docs/_build/html/$(NC)"; \ - else \ - echo "$(YELLOW)No docs directory found. Skipping documentation generation.$(NC)"; \ - fi - -# Full development pipeline + @echo "$(GREEN)Building MkDocs site...$(NC)" + cd .. && mkdocs build + @echo "$(GREEN)Site generated in ../site/$(NC)" + +# Serve the MkDocs site locally with auto-reload. +.PHONY: docs-serve +docs-serve: + @echo "$(GREEN)Serving MkDocs site at http://127.0.0.1:8000 ...$(NC)" + cd .. && mkdocs serve + +# Full development pipeline. Uses `install-dev` (not `install`) so that +# `lint`, `type-check`, and `test` have flake8 / mypy / pytest available +# when this is run from a fresh checkout. .PHONY: all -all: install lint type-check test build validate +all: install-dev lint type-check test build validate @echo "$(GREEN)Full pipeline completed successfully!$(NC)" -# Development workflow targets +# Development workflow target. +# +# Installs every dev dependency *into the venv* using `$(VENV_DIR)/bin/pip` +# directly — do not rely on `$(PIP)` here, because Make resolves variables at +# parse time and the venv may not have existed yet when this Makefile was +# read. .PHONY: dev-setup -dev-setup: venv install-dev install +dev-setup: venv + @echo "$(GREEN)Installing project (editable) + dev dependencies into $(VENV_DIR)/...$(NC)" + $(VENV_DIR)/bin/pip install --upgrade pip + $(VENV_DIR)/bin/pip install -e ".[tests]" + $(VENV_DIR)/bin/pip install $(DEV_TOOLS) @echo "$(GREEN)Development environment setup completed!$(NC)" - @echo "$(YELLOW)Don't forget to activate the virtual environment:$(NC)" - @echo "source $(VENV_DIR)/bin/activate" + @echo "" + @echo "$(YELLOW)You can now run targets directly — they auto-detect $(VENV_DIR)/.$(NC)" + @echo "$(YELLOW)To activate the venv in your shell anyway:$(NC) source $(VENV_DIR)/bin/activate" .PHONY: pre-commit pre-commit: lint type-check test @@ -246,12 +294,16 @@ pre-commit: lint type-check test pre-publish: all security @echo "$(GREEN)Pre-publish checks completed!$(NC)" -# CI/CD targets +# CI/CD target. `install-dev` already installs the package in editable mode +# via `pip install -e ".[tests]"`, so a separate `install` step would be +# redundant. .PHONY: ci -ci: install-dev install lint type-check test build validate +ci: install-dev lint type-check test build validate @echo "$(GREEN)CI pipeline completed!$(NC)" -# Show package info +# Show package info. The grep filter lists every tool in $(DEV_TOOLS) plus +# the package itself and `pytest` (installed via the `[tests]` extra). Keep +# the regex in sync if $(DEV_TOOLS) changes. .PHONY: info info: @echo "$(GREEN)Package Information:$(NC)" @@ -261,7 +313,7 @@ info: @echo "Pip: $(shell $(PIP) --version)" @echo "" @echo "$(GREEN)Installed packages:$(NC)" - @$(PIP) list | grep -E "($(PACKAGE_NAME)|pytest|flake8|black|mypy|twine|build)" + @$(PIP) list | grep -E "($(PACKAGE_NAME)|pytest|pytest-cov|flake8|black|mypy|twine|build|hatch|pip-audit)" # Quick release workflow .PHONY: release diff --git a/python/README.md b/python/README.md index 3cdeb6d..a14a185 100644 --- a/python/README.md +++ b/python/README.md @@ -21,7 +21,7 @@ $ pip install FlightRadarAPI ## Basic Usage: Import the class `FlightRadar24API` and create an instance of it. ```py -from FlightRadar24 import FlightRadar24API +from FlightRadarAPI import FlightRadar24API fr_api = FlightRadar24API() ``` diff --git a/python/pyproject.toml b/python/pyproject.toml index 97b9293..68458ec 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -3,7 +3,7 @@ name = "FlightRadarAPI" dynamic = ["version"] description = "SDK for FlightRadar24" authors = [ - { name = "Jean Loui Bernard Silva de Jesus", email = "jeanextreme002@gmail.com" }, + { name = "Jean Loui Bernard Silva de Jesus", email = "contact@jeanloui.dev" }, ] license = "MIT" readme = "README.md" @@ -27,7 +27,12 @@ dependencies = [ exclude = ["tests", ".flake8"] [tool.hatch.build.targets.wheel] -packages = ["FlightRadar24"] +packages = ["FlightRadarAPI", "FlightRadar24"] + +# Force-include the PEP 561 marker so downstream type checkers always see it, +# regardless of future changes to the global exclude list above. +[tool.hatch.build.targets.wheel.force-include] +"FlightRadarAPI/py.typed" = "FlightRadarAPI/py.typed" [project.optional-dependencies] tests = [ @@ -41,7 +46,7 @@ tests = [ "Bug Reports" = "https://github.com/JeanExtreme002/FlightRadarAPI/issues" [tool.hatch.version] -path = "FlightRadar24/__init__.py" +path = "FlightRadarAPI/__init__.py" [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/python/tests/package.py b/python/tests/package.py index 03fc31d..11d2331 100644 --- a/python/tests/package.py +++ b/python/tests/package.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from FlightRadar24 import __version__ as version -from FlightRadar24 import Countries, FlightRadar24API -from FlightRadar24.errors import CloudflareError +from FlightRadarAPI import __version__ as version +from FlightRadarAPI import Countries, FlightRadar24API +from FlightRadarAPI.errors import CloudflareError diff --git a/python/tests/test_api.py b/python/tests/test_api.py index 973ff2b..5c8e0a4 100644 --- a/python/tests/test_api.py +++ b/python/tests/test_api.py @@ -2,8 +2,8 @@ import pytest -from FlightRadar24 import Entity, Flight -from FlightRadar24.errors import CloudflareError, LoginError +from FlightRadarAPI import Entity, Flight +from FlightRadarAPI.errors import CloudflareError, LoginError from package import Countries, FlightRadar24API, version from util import repeat_test diff --git a/python/tests/test_legacy_import.py b/python/tests/test_legacy_import.py new file mode 100644 index 0000000..2cd78c8 --- /dev/null +++ b/python/tests/test_legacy_import.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +"""Backwards-compatibility tests for the deprecated `FlightRadar24` alias. + +The package was renamed to `FlightRadarAPI`, but `FlightRadar24` is kept as +a thin shim so existing user code keeps working through one release. These +tests guard the deprecation contract so that removing the shim later is a +deliberate decision, not an accidental regression. +""" + +import importlib +import sys +import warnings + + +def _reimport(name: str): + """Drop any cached copy of the module so `import` re-runs side effects.""" + for key in list(sys.modules): + if key == name or key.startswith(f"{name}."): + del sys.modules[key] + return importlib.import_module(name) + + +class TestLegacyAlias: + def test_import_emits_deprecation_warning(self): + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + _reimport("FlightRadar24") + deprecations = [w for w in caught if issubclass(w.category, DeprecationWarning)] + assert deprecations, "Expected a DeprecationWarning from FlightRadar24 import" + assert "FlightRadarAPI" in str(deprecations[0].message) + + def test_public_api_is_re_exported(self): + legacy = _reimport("FlightRadar24") + new = importlib.import_module("FlightRadarAPI") + assert legacy.FlightRadar24API is new.FlightRadar24API + assert legacy.Countries is new.Countries + assert legacy.__version__ == new.__version__ + + def test_submodule_imports_resolve_to_new_package(self): + _reimport("FlightRadar24") + # Legacy submodules are registered dynamically via sys.modules in the + # FlightRadar24 shim, so static analyzers cannot resolve them. + from FlightRadar24.errors import CloudflareError as LegacyError # type: ignore[import-not-found] + from FlightRadarAPI.errors import CloudflareError as NewError + assert LegacyError is NewError + + def test_nested_subpackage_imports_resolve(self): + _reimport("FlightRadar24") + from FlightRadar24.entities.airport import Airport as LegacyAirport # type: ignore[import-not-found] + from FlightRadarAPI.entities.airport import Airport as NewAirport + assert LegacyAirport is NewAirport diff --git a/python/tests/test_parsers_offline.py b/python/tests/test_parsers_offline.py index bed99fa..77c3b22 100644 --- a/python/tests/test_parsers_offline.py +++ b/python/tests/test_parsers_offline.py @@ -10,7 +10,7 @@ import os -from FlightRadar24.parsers import parse_airlines_html, parse_airports_html +from FlightRadarAPI.parsers import parse_airlines_html, parse_airports_html FIXTURES = os.path.join(os.path.dirname(__file__), "fixtures") diff --git a/python/tests/test_request_policy.py b/python/tests/test_request_policy.py index c22d95e..22a86e0 100644 --- a/python/tests/test_request_policy.py +++ b/python/tests/test_request_policy.py @@ -17,9 +17,9 @@ import pytest -from FlightRadar24 import request as request_module -from FlightRadar24.errors import CloudflareError -from FlightRadar24.request import APIRequest, RetryPolicy, _run_with_retry +from FlightRadarAPI import request as request_module +from FlightRadarAPI.errors import CloudflareError +from FlightRadarAPI.request import APIRequest, RetryPolicy, _run_with_retry from _request_doubles import FakeResponse, StubSession diff --git a/python/tests/test_request_transport.py b/python/tests/test_request_transport.py index a2c50ca..03f0187 100644 --- a/python/tests/test_request_transport.py +++ b/python/tests/test_request_transport.py @@ -24,7 +24,7 @@ import pytest -from FlightRadar24.request import APIRequest +from FlightRadarAPI.request import APIRequest from _request_doubles import FakeResponse, StubSession diff --git a/python/tests/test_snapshots.py b/python/tests/test_snapshots.py index 7186d23..644f1d1 100644 --- a/python/tests/test_snapshots.py +++ b/python/tests/test_snapshots.py @@ -2,7 +2,7 @@ import pytest -from FlightRadar24.errors import CloudflareError +from FlightRadarAPI.errors import CloudflareError from package import Countries, FlightRadar24API from util import repeat_test