`download()` used module-level dicts in `yfinance.shared` (`_DFS`,
`_ERRORS`, `_TRACEBACKS`, `_ISINS`) as scratch space. Concurrent calls
trampled each other; the previous fix wrapped the whole call in
`shared._LOCK`, which silently serialized parallel downloads — hiding
the race instead of removing it, and defeating the parallelism users
reach for `download()` to get.
- Introduce `_DownloadCtx` per call holding `dfs/errors/tracebacks/isins`
plus a fine-grained lock for thread workers.
- `_download_one`, `_download_one_threaded`, `_realign_dfs` take the ctx
explicitly and write into it.
- Drop the call-wide `shared._LOCK.acquire()`; concurrent `download()`
calls now actually run in parallel. The polling loop on the threaded
path reads `len(ctx.dfs)` under the ctx lock.
- `PriceHistory` records soft errors (delisted, missing tz, repair
failure) in `self._last_error` instead of `shared._ERRORS`; the
download loop reads it after `Ticker.history()` returns.
- `yfinance.shared` is left in place for import-compat with code that
may still import it, but yfinance no longer reads or writes it.
- Update existing test_multi to the new `_download_one(ctx, ...)`
signature and add regression tests covering result separation,
no-raise on concurrent calls, and a sentinel proving `shared._DFS`
is no longer used as scratch space.
Yahoo's `/v1/finance/sectors/{key}` and `/industries/{key}` endpoints accept
an ISO 3166-1 alpha-2 `region` query parameter and return regional top
companies/ETFs/funds accordingly. The Domain base class hard-coded `region=US`,
so `Sector("technology").top_companies` always returned U.S. names regardless
of the user's interest.
- Add `region: str = "US"` to `Domain.__init__` and propagate through
`Sector` and `Industry`.
- Normalize input via `.strip().upper()` so "us", " GB ", and "Fr" all work.
- Use `self._region` in the request params.
- Document and test the behavior.
Closes#2601
Yahoo's markettime endpoint silently ignores the `market` parameter and
always returns U.S. data, so `Market("EUROPE").status` previously returned
U.S. status while pretending to be European. The summary endpoint does
honor the parameter and is unaffected.
- Introduce `MarketRegion(str, Enum)` as the single source of truth for
the supported region values, exposed as `yfinance.MarketRegion`.
- Validate `Market(...)` input against the enum and raise `ValueError`
on unknown regions.
- Detect the markettime mismatch: when a non-`US` region is requested
but the response id is `us`, set `status` to `None` and log a warning
rather than returning misleading data.
- Document the markettime limitation and add tests covering validation
and the EUROPE/US fetch behavior.
Closes#2784
Guard both chart-error and chart-result checks against data['chart']
being None, which previously raised TypeError instead of the expected
YFPricesMissingError.
- history.py L270: add 'data["chart"]' truthiness check before accessing ['error']
- history.py L274: add 'not data["chart"]' short-circuit before accessing ['result']
- Add regression test covering chart=None, result=None, missing key, and error payload
The "1mo" branch compared only `dt1.month == dt2.month`, so e.g.
(2024-12-15, 2025-12-15) returned True. The "3mo" branch already
handles year differences via `year_diff`; mirror that by also
requiring `dt1.year == dt2.year`.
Added a test (`test_same_month_different_year`) that fails on the
old logic and passes on the fix. All existing tests still pass.