pypsx-toolkit 3.0.2__tar.gz → 3.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/PKG-INFO +10 -20
  2. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/README.md +3 -12
  3. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pyproject.toml +24 -14
  4. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/__init__.py +86 -14
  5. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/analysis/__init__.py +10 -1
  6. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/api.pyi +0 -2
  7. pypsx_toolkit-3.0.4/setup.py +103 -0
  8. pypsx_toolkit-3.0.2/setup.py +0 -70
  9. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/LICENSE +0 -0
  10. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/MANIFEST.in +0 -0
  11. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/__init__.pyi +0 -0
  12. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/analysis/__init__.pyi +0 -0
  13. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/core/__init__.py +0 -0
  14. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/core/errors.pyi +0 -0
  15. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/core/stream.pyi +0 -0
  16. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/endpoints/__init__.py +0 -0
  17. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/endpoints/constants.py +0 -0
  18. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/format/__init__.py +0 -0
  19. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/market.pyi +0 -0
  20. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/models.py +0 -0
  21. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/models.pyi +0 -0
  22. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/py.typed +0 -0
  23. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit/ticker.pyi +0 -0
  24. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/pypsx_toolkit.egg-info/SOURCES.txt +0 -0
  25. {pypsx_toolkit-3.0.2 → pypsx_toolkit-3.0.4}/setup.cfg +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypsx-toolkit
3
- Version: 3.0.2
3
+ Version: 3.0.4
4
4
  Summary: Pakistan Stock Exchange (PSX) data library with yfinance-like API
5
- Author-email: PyPSX Team <contact@pypsx.dev>
6
- Maintainer-email: PyPSX Team <contact@pypsx.dev>
5
+ Author-email: PyPSX Team <pypsxofficial@gmail.com>
6
+ Maintainer-email: PyPSX Team <pypsxofficial@gmail.com>
7
7
  License: Copyright (c) 2026 PyPSX Team. All rights reserved.
8
8
 
9
9
  This software and associated documentation files (the "Software") are the
@@ -32,8 +32,6 @@ Classifier: Intended Audience :: Financial and Insurance Industry
32
32
  Classifier: License :: Other/Proprietary License
33
33
  Classifier: Operating System :: OS Independent
34
34
  Classifier: Programming Language :: Python :: 3
35
- Classifier: Programming Language :: Python :: 3.8
36
- Classifier: Programming Language :: Python :: 3.9
37
35
  Classifier: Programming Language :: Python :: 3.10
38
36
  Classifier: Programming Language :: Python :: 3.11
39
37
  Classifier: Programming Language :: Python :: 3.12
@@ -41,15 +39,16 @@ Classifier: Programming Language :: Python :: 3 :: Only
41
39
  Classifier: Topic :: Office/Business :: Financial
42
40
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
43
41
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
44
- Requires-Python: >=3.8
42
+ Requires-Python: >=3.10
45
43
  Description-Content-Type: text/markdown
46
44
  License-File: LICENSE
47
45
  Requires-Dist: requests>=2.31.0
48
46
  Requires-Dist: beautifulsoup4>=4.12.0
49
47
  Requires-Dist: lxml>=4.9.0
50
- Requires-Dist: pandas>=2.0.0
48
+ Requires-Dist: pandas<2.3.3,>=2.0.0
51
49
  Requires-Dist: loguru>=0.7.0
52
- Requires-Dist: numpy>=1.24.0
50
+ Requires-Dist: numpy<2.3.0,>=1.24.0
51
+ Requires-Dist: tqdm>=4.66.0
53
52
  Provides-Extra: plot
54
53
  Requires-Dist: matplotlib>=3.5.0; extra == "plot"
55
54
  Requires-Dist: seaborn>=0.11.0; extra == "plot"
@@ -67,7 +66,7 @@ Dynamic: license-file
67
66
 
68
67
  # pypsx-toolkit — Pakistan Stock Exchange Data Library
69
68
 
70
- A clean, simple Python library to fetch and analyze Pakistan Stock Exchange (PSX) data from official DPS endpoints. Get real-time market data, historical prices, and powerful analysis tools all in one package — no authentication required.
69
+ A clean, simple Python library to fetch and analyze Pakistan Stock Exchange (PSX) market data. Get real-time market data, historical prices, and powerful analysis tools all in one package — no authentication required.
71
70
 
72
71
  ## Installation
73
72
 
@@ -505,17 +504,6 @@ historical = ticker.get_historical(
505
504
  print(historical.head())
506
505
  ```
507
506
 
508
- ## Data Sources
509
-
510
- PyPSX Toolkit uses official Pakistan Stock Exchange DPS endpoints:
511
- - Market Watch: `https://dps.psx.com.pk/market-watch`
512
- - Sector Summary: `https://dps.psx.com.pk/sector-summary/sectorwise`
513
- - Indices: `https://dps.psx.com.pk/indices/{INDEX}`
514
- - Trading Board: `https://dps.psx.com.pk/trading-board/REG/main`
515
- - Performers: `https://dps.psx.com.pk/performers`
516
- - Timeseries: `https://dps.psx.com.pk/timeseries/int/{SYMBOL}`, `.../eod/{SYMBOL}`
517
- - Historical OHLCV: `https://dps.psx.com.pk/historical`
518
-
519
507
  ## Examples
520
508
 
521
509
  ### Example 1: Find Top Volume Stocks
@@ -604,6 +592,7 @@ for insight in analysis["portfolio_insights"]:
604
592
 
605
593
  ### PSXTicker Class
606
594
 
595
+ <!-- pypsx-test: skip: API signature reference, not executable code (shows type annotations in call position) -->
607
596
  ```python
608
597
  ticker = pypsx_toolkit.PSXTicker(symbol: str)
609
598
 
@@ -649,6 +638,7 @@ pypsx_toolkit.get_snapshot(symbol) # Get snapshot data from all tab
649
638
 
650
639
  ### Download Functions
651
640
 
641
+ <!-- pypsx-test: skip: API signature reference; parameter names are placeholders, not bound values -->
652
642
  ```python
653
643
  pypsx_toolkit.download(symbols, period="1y", interval="1d") # Batch download
654
644
  pypsx_toolkit.get_intraday_multiple(symbols) # Multiple intraday
@@ -1,6 +1,6 @@
1
1
  # pypsx-toolkit — Pakistan Stock Exchange Data Library
2
2
 
3
- A clean, simple Python library to fetch and analyze Pakistan Stock Exchange (PSX) data from official DPS endpoints. Get real-time market data, historical prices, and powerful analysis tools all in one package — no authentication required.
3
+ A clean, simple Python library to fetch and analyze Pakistan Stock Exchange (PSX) market data. Get real-time market data, historical prices, and powerful analysis tools all in one package — no authentication required.
4
4
 
5
5
  ## Installation
6
6
 
@@ -438,17 +438,6 @@ historical = ticker.get_historical(
438
438
  print(historical.head())
439
439
  ```
440
440
 
441
- ## Data Sources
442
-
443
- PyPSX Toolkit uses official Pakistan Stock Exchange DPS endpoints:
444
- - Market Watch: `https://dps.psx.com.pk/market-watch`
445
- - Sector Summary: `https://dps.psx.com.pk/sector-summary/sectorwise`
446
- - Indices: `https://dps.psx.com.pk/indices/{INDEX}`
447
- - Trading Board: `https://dps.psx.com.pk/trading-board/REG/main`
448
- - Performers: `https://dps.psx.com.pk/performers`
449
- - Timeseries: `https://dps.psx.com.pk/timeseries/int/{SYMBOL}`, `.../eod/{SYMBOL}`
450
- - Historical OHLCV: `https://dps.psx.com.pk/historical`
451
-
452
441
  ## Examples
453
442
 
454
443
  ### Example 1: Find Top Volume Stocks
@@ -537,6 +526,7 @@ for insight in analysis["portfolio_insights"]:
537
526
 
538
527
  ### PSXTicker Class
539
528
 
529
+ <!-- pypsx-test: skip: API signature reference, not executable code (shows type annotations in call position) -->
540
530
  ```python
541
531
  ticker = pypsx_toolkit.PSXTicker(symbol: str)
542
532
 
@@ -582,6 +572,7 @@ pypsx_toolkit.get_snapshot(symbol) # Get snapshot data from all tab
582
572
 
583
573
  ### Download Functions
584
574
 
575
+ <!-- pypsx-test: skip: API signature reference; parameter names are placeholders, not bound values -->
585
576
  ```python
586
577
  pypsx_toolkit.download(symbols, period="1y", interval="1d") # Batch download
587
578
  pypsx_toolkit.get_intraday_multiple(symbols) # Multiple intraday
@@ -4,16 +4,18 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pypsx-toolkit"
7
- version = "3.0.2"
7
+ version = "3.0.4"
8
8
  description = "Pakistan Stock Exchange (PSX) data library with yfinance-like API"
9
9
  readme = "README.md"
10
- requires-python = ">=3.8"
10
+ # 3.10 is the real floor: `pandas>=2.0` already excludes 3.8/3.9, and
11
+ # core/utils.py uses PEP 604 (`int | None`) in a runtime signature.
12
+ requires-python = ">=3.10"
11
13
  license = { file = "LICENSE" }
12
14
  authors = [
13
- { name = "PyPSX Team", email = "contact@pypsx.dev" }
15
+ { name = "PyPSX Team", email = "pypsxofficial@gmail.com" }
14
16
  ]
15
17
  maintainers = [
16
- { name = "PyPSX Team", email = "contact@pypsx.dev" }
18
+ { name = "PyPSX Team", email = "pypsxofficial@gmail.com" }
17
19
  ]
18
20
  keywords = ["psx", "pakistan", "stock", "finance", "data", "api", "pandas", "yfinance", "market-data"]
19
21
  classifiers = [
@@ -23,8 +25,6 @@ classifiers = [
23
25
  "License :: Other/Proprietary License",
24
26
  "Operating System :: OS Independent",
25
27
  "Programming Language :: Python :: 3",
26
- "Programming Language :: Python :: 3.8",
27
- "Programming Language :: Python :: 3.9",
28
28
  "Programming Language :: Python :: 3.10",
29
29
  "Programming Language :: Python :: 3.11",
30
30
  "Programming Language :: Python :: 3.12",
@@ -38,9 +38,13 @@ dependencies = [
38
38
  "requests>=2.31.0",
39
39
  "beautifulsoup4>=4.12.0",
40
40
  "lxml>=4.9.0",
41
- "pandas>=2.0.0",
41
+ "pandas>=2.0.0,<2.3.3",
42
42
  "loguru>=0.7.0",
43
- "numpy>=1.24.0",
43
+ "numpy>=1.24.0,<2.3.0",
44
+ # Imported by api.py, endpoints/historical.py and core/utils.py. Those imports
45
+ # are try/except-guarded with a no-op fallback, but the fallback path is
46
+ # untested and every target environment (including Colab) already ships tqdm.
47
+ "tqdm>=4.66.0",
44
48
  ]
45
49
 
46
50
  [project.optional-dependencies]
@@ -61,8 +65,12 @@ Homepage = "https://pypsx.com"
61
65
  Documentation = "https://markets.pypsx.com/docs"
62
66
  Changelog = "https://markets.pypsx.com/docs/changelog"
63
67
 
64
- [project.scripts]
65
- pypsx-toolkit = "pypsx_toolkit:quick_start"
68
+ # NOTE: no [project.scripts]. This previously declared
69
+ # pypsx-toolkit = "pypsx_toolkit:quick_start"
70
+ # but `quick_start` does not exist anywhere in the package, so the installed
71
+ # console script raised AttributeError on every invocation. This library has no
72
+ # CLI; removing the entry point is the honest fix. If a CLI is wanted later,
73
+ # implement the target function first.
66
74
 
67
75
  [tool.setuptools]
68
76
  include-package-data = true
@@ -152,8 +160,10 @@ markers = [
152
160
  "integration: marks tests as integration tests",
153
161
  "unit: marks tests as unit tests",
154
162
  ]
155
- filterwarnings = [
156
- "ignore::DeprecationWarning",
157
- "ignore::PendingDeprecationWarning",
158
- ]
163
+ # NOTE: deliberately no blanket `filterwarnings = ["ignore::DeprecationWarning"]`
164
+ # here. The Colab-mirror suite (b2c/tests/colab_mirror/) runs under
165
+ # `filterwarnings = error` because a warning in a notebook cell is a user-visible
166
+ # defect. A blanket ignore at this level hides exactly the class of bug that
167
+ # suite exists to catch. Allow-list individual warnings there, with an owner and
168
+ # an expiry, instead of re-adding a catch-all.
159
169
 
@@ -2,9 +2,9 @@
2
2
  PSX yfinance-style API
3
3
  """
4
4
 
5
- __version__ = "3.0.2"
5
+ __version__ = "3.0.4"
6
6
  __author__ = "PyPSX Team"
7
- __email__ = "pypsx@example.com"
7
+ __email__ = "pypsxofficial@gmail.com"
8
8
 
9
9
  from pypsx_toolkit.ticker import PSXTicker, Ticker
10
10
 
@@ -37,6 +37,14 @@ from pypsx_toolkit.endpoints.sectors import get_sector_constituents as _get_sect
37
37
  from pypsx_toolkit.endpoints.compliant_listings import get_symbols_by_sector as _get_symbols_by_sector
38
38
  from pypsx_toolkit.endpoints.snapshot import get_snapshot
39
39
 
40
+ # Financial reports. Implemented and working, but previously not reachable from
41
+ # anywhere in the public surface.
42
+ from pypsx_toolkit.endpoints.reports import get_reports
43
+
44
+ # Cache control. Useful in notebooks, where a stale cached response is a common
45
+ # source of "why is my data wrong" confusion.
46
+ from pypsx_toolkit.core.cache import clear_pypsx_cache, get_cache_stats
47
+
40
48
  from pypsx_toolkit.market import (
41
49
  top_performers,
42
50
  sector_summary,
@@ -70,18 +78,29 @@ from pypsx_toolkit.models import (
70
78
  DividendHistory,
71
79
  )
72
80
 
73
- # Analysis module (optional - requires analysis package)
74
- try:
75
- from pypsx_toolkit.analysis import (
76
- interpret_stock,
77
- sharpe_ratio,
78
- rsi,
79
- macd,
80
- bollinger_bands,
81
- )
82
- except ImportError:
83
- # Analysis module not available
84
- pass
81
+ # Exceptions. Previously these were reachable only via the private
82
+ # `pypsx_toolkit.core.errors` path, which the docs told users to import from.
83
+ # Re-exported here as the supported location; the old path still works.
84
+ from pypsx_toolkit.core.errors import (
85
+ PSXError,
86
+ PSXHTTPError,
87
+ PSXTimeoutError,
88
+ PSXNotFoundError,
89
+ PSXScopeError,
90
+ )
91
+
92
+ # Analysis module. This is NOT optional -- it depends only on pandas and numpy,
93
+ # both hard requirements of this package. It was previously wrapped in a
94
+ # `try/except ImportError: pass`, which silently turned any genuine bug inside
95
+ # the analysis package into a confusing AttributeError at call time.
96
+ from pypsx_toolkit import analysis
97
+ from pypsx_toolkit.analysis import (
98
+ interpret_stock,
99
+ sharpe_ratio,
100
+ rsi,
101
+ macd,
102
+ bollinger_bands,
103
+ )
85
104
 
86
105
  __all__ = [
87
106
  "__version__",
@@ -129,6 +148,14 @@ __all__ = [
129
148
  "get_sector_constituents",
130
149
  "get_symbols_by_sector",
131
150
  "get_snapshot",
151
+ "get_reports",
152
+ # Cache control
153
+ "clear_pypsx_cache",
154
+ "get_cache_stats",
155
+ # NOTE: `charts` is deliberately NOT listed here. It resolves lazily via
156
+ # __getattr__ and needs the optional `[plot]` extra, so including it would
157
+ # make `from pypsx_toolkit import *` raise ImportError on a plain install.
158
+ # Access it as `pypsx_toolkit.charts` (see the Charting docs).
132
159
  # Models
133
160
  "SymbolInfo",
134
161
  "SectorSummary",
@@ -147,8 +174,53 @@ __all__ = [
147
174
  "Announcement",
148
175
  "DividendInfo",
149
176
  "DividendHistory",
177
+ # Exceptions. Also available at the original `pypsx_toolkit.core.errors` path.
178
+ "PSXError",
179
+ "PSXHTTPError",
180
+ "PSXTimeoutError",
181
+ "PSXNotFoundError",
182
+ "PSXScopeError",
183
+ # Analysis. `analysis` is the submodule (documented usage is
184
+ # `import pypsx_toolkit.analysis as pa`); the rest are convenience
185
+ # re-exports that were importable before but absent from __all__.
186
+ "analysis",
187
+ "interpret_stock",
188
+ "sharpe_ratio",
189
+ "rsi",
190
+ "macd",
191
+ "bollinger_bands",
150
192
  ]
151
193
 
194
+ # `market_watch_func` / `get_indices_func` are internal aliases created to avoid
195
+ # colliding with the same-named api.py functions. They stay bound for backward
196
+ # compatibility but are deliberately excluded from __all__ above -- they are not
197
+ # part of the supported surface. Use `market_watch()` / `get_indices()`.
198
+
199
+
200
+ def __getattr__(name: str):
201
+ """
202
+ Lazily expose `pypsx_toolkit.charts`.
203
+
204
+ The charting helpers need matplotlib/seaborn, which ship only under the
205
+ optional `[plot]` extra. Importing them eagerly would make a plain
206
+ `pip install pypsx-toolkit` fail at `import pypsx_toolkit`, so resolution is
207
+ deferred until first access and a missing extra produces an actionable
208
+ message instead of a bare ModuleNotFoundError.
209
+ """
210
+ if name == "charts":
211
+ import sys
212
+
213
+ try:
214
+ from pypsx_toolkit.endpoints import charts as _charts
215
+ except ImportError as exc: # pragma: no cover - depends on install extras
216
+ raise ImportError(
217
+ "pypsx_toolkit.charts requires the optional plotting dependencies. "
218
+ "Install them with: pip install 'pypsx-toolkit[plot]'"
219
+ ) from exc
220
+ setattr(sys.modules[__name__], "charts", _charts)
221
+ return _charts
222
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
223
+
152
224
 
153
225
  # Legacy compatibility wrappers used by audit script (clean outputs only)
154
226
 
@@ -70,7 +70,16 @@ __all__ = [
70
70
 
71
71
  # Insight engine
72
72
  'interpret_stock', 'interpret_portfolio', 'detect_patterns',
73
- 'generate_trading_signals', 'market_sentiment_analysis'
73
+ 'generate_trading_signals', 'market_sentiment_analysis',
74
+
75
+ # Composite helpers (defined in this module, below)
76
+ 'quick_analysis', 'portfolio_analysis',
77
+
78
+ # Short aliases (defined at the bottom of this module). These are used in
79
+ # the published documentation, so they are part of the supported surface.
80
+ 'ma', 'ema', 'bb', 'sharpe', 'sortino', 'calmar', 'cum_returns', 'max_dd',
81
+ 'win_rate_pct', 'pl_ratio', 'recovery', 'perf_summary', 'interpret',
82
+ 'patterns', 'signals', 'sentiment',
74
83
  ]
75
84
 
76
85
 
@@ -61,8 +61,6 @@ def get_intraday(symbol: str) -> pd.DataFrame:
61
61
  """
62
62
  Fetch intraday tick data for the last 2 trading days.
63
63
 
64
- Runs on the user's local machine to distribute scraping traffic.
65
-
66
64
  Args:
67
65
  symbol: PSX ticker symbol.
68
66
 
@@ -0,0 +1,103 @@
1
+ """
2
+ Cython build configuration for pypsx.
3
+
4
+ Compiles all implementation modules into platform-specific binary extensions
5
+ (.pyd on Windows, .so on Linux/macOS). __init__.py, models.py, and constants.py
6
+ are intentionally excluded so they remain as plain-text Python re-exporters.
7
+ """
8
+ import os
9
+ import glob
10
+ from setuptools import setup, Extension, find_packages
11
+ from setuptools.command.build_py import build_py as _build_py
12
+ from Cython.Build import cythonize
13
+
14
+ # Stems that must stay as plain Python (pickling, isinstance, re-exports)
15
+ EXCLUDE_STEMS = {"__init__", "constants", "models"}
16
+
17
+ # Folders to skip entirely
18
+ EXCLUDE_DIRS = {"backend", "build", "dist", "tests", "__pycache__", ".git"}
19
+
20
+
21
+ # os.name == "nt" does not imply MSVC -- CC/CXX may point at a mingw
22
+ # toolchain (e.g. w64devkit) for local builds without Visual Studio, and
23
+ # mingw's gcc/g++ reject MSVC-style "/O2" the same way MSVC would reject "-O2".
24
+ _USING_MSVC = os.name == "nt" and "gcc" not in os.environ.get("CC", "").lower()
25
+ _OPT_FLAG = "/O2" if _USING_MSVC else "-O2"
26
+
27
+
28
+ def collect_extensions(pkg_root: str):
29
+ extensions = []
30
+ for path in glob.glob(f"{pkg_root}/**/*.py", recursive=True):
31
+ # Normalise separators
32
+ norm = path.replace("\\", "/")
33
+
34
+ # Skip excluded directories
35
+ parts = norm.split("/")
36
+ if any(p in EXCLUDE_DIRS for p in parts):
37
+ continue
38
+
39
+ stem = os.path.splitext(os.path.basename(path))[0]
40
+ if stem in EXCLUDE_STEMS:
41
+ continue
42
+
43
+ # Convert path to dotted module name (e.g. pypsx/core/fetchers.py -> pypsx.core.fetchers)
44
+ module = norm.replace("/", ".")[:-3]
45
+ extensions.append(
46
+ Extension(
47
+ module,
48
+ [path],
49
+ extra_compile_args=[_OPT_FLAG],
50
+ )
51
+ )
52
+ return extensions
53
+
54
+
55
+ class build_py(_build_py):
56
+ """
57
+ Drop .py source for modules that get Cython-compiled, so wheels ship only
58
+ the compiled extension (+ .pyi stub) for them -- MANIFEST.in's
59
+ include/exclude rules only govern sdist content, NOT bdist_wheel, so
60
+ without this override every wheel ships the full readable .py source
61
+ right alongside the .pyd/.so, silently defeating the whole point of
62
+ compiling in the first place.
63
+ """
64
+
65
+ def find_package_modules(self, package, package_dir):
66
+ modules = super().find_package_modules(package, package_dir)
67
+ return [(pkg, mod, path) for (pkg, mod, path) in modules if mod in EXCLUDE_STEMS]
68
+
69
+
70
+ def main():
71
+ extensions = collect_extensions("pypsx_toolkit")
72
+
73
+ setup(
74
+ cmdclass={"build_py": build_py},
75
+ packages=find_packages(
76
+ exclude=["backend*", "build*", "dist*", "tests*", "*.egg-info*"]
77
+ ),
78
+ ext_modules=cythonize(
79
+ extensions,
80
+ compiler_directives={
81
+ "language_level": "3",
82
+ # binding=True: wraps compiled functions so inspect.signature() works.
83
+ # Required if FastAPI or any DI framework introspects function args.
84
+ "binding": True,
85
+ # embedsignature=True: embeds the Python call signature in the docstring
86
+ # so IDEs can show it even when the .pyi stub is absent.
87
+ "embedsignature": True,
88
+ },
89
+ build_dir="build",
90
+ annotate=False,
91
+ nthreads=4,
92
+ ),
93
+ package_data={"": ["*.pyi", "py.typed"]},
94
+ zip_safe=False,
95
+ )
96
+
97
+
98
+ if __name__ == "__main__":
99
+ # cythonize(nthreads=4) spawns a multiprocessing pool. On Windows
100
+ # (spawn start method) child processes reimport this file as __main__;
101
+ # without this guard, each child re-enters main() and re-spawns its own
102
+ # pool, crashing the whole build with BrokenProcessPool.
103
+ main()
@@ -1,70 +0,0 @@
1
- """
2
- Cython build configuration for pypsx.
3
-
4
- Compiles all implementation modules into platform-specific binary extensions
5
- (.pyd on Windows, .so on Linux/macOS). __init__.py, models.py, and constants.py
6
- are intentionally excluded so they remain as plain-text Python re-exporters.
7
- """
8
- import os
9
- import glob
10
- from setuptools import setup, Extension, find_packages
11
- from Cython.Build import cythonize
12
-
13
- # Stems that must stay as plain Python (pickling, isinstance, re-exports)
14
- EXCLUDE_STEMS = {"__init__", "constants", "models"}
15
-
16
- # Folders to skip entirely
17
- EXCLUDE_DIRS = {"backend", "build", "dist", "tests", "__pycache__", ".git"}
18
-
19
-
20
- def collect_extensions(pkg_root: str):
21
- extensions = []
22
- for path in glob.glob(f"{pkg_root}/**/*.py", recursive=True):
23
- # Normalise separators
24
- norm = path.replace("\\", "/")
25
-
26
- # Skip excluded directories
27
- parts = norm.split("/")
28
- if any(p in EXCLUDE_DIRS for p in parts):
29
- continue
30
-
31
- stem = os.path.splitext(os.path.basename(path))[0]
32
- if stem in EXCLUDE_STEMS:
33
- continue
34
-
35
- # Convert path to dotted module name (e.g. pypsx/core/fetchers.py -> pypsx.core.fetchers)
36
- module = norm.replace("/", ".")[:-3]
37
- extensions.append(
38
- Extension(
39
- module,
40
- [path],
41
- extra_compile_args=["/O2"] if os.name == "nt" else ["-O2"],
42
- )
43
- )
44
- return extensions
45
-
46
-
47
- extensions = collect_extensions("pypsx_toolkit")
48
-
49
- setup(
50
- packages=find_packages(
51
- exclude=["backend*", "build*", "dist*", "tests*", "*.egg-info*"]
52
- ),
53
- ext_modules=cythonize(
54
- extensions,
55
- compiler_directives={
56
- "language_level": "3",
57
- # binding=True: wraps compiled functions so inspect.signature() works.
58
- # Required if FastAPI or any DI framework introspects function args.
59
- "binding": True,
60
- # embedsignature=True: embeds the Python call signature in the docstring
61
- # so IDEs can show it even when the .pyi stub is absent.
62
- "embedsignature": True,
63
- },
64
- build_dir="build",
65
- annotate=False,
66
- nthreads=4,
67
- ),
68
- package_data={"": ["*.pyi", "py.typed"]},
69
- zip_safe=False,
70
- )
File without changes
File without changes
File without changes