lixinger-python 0.1.2__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 (162) hide show
  1. lixinger_python-0.1.2/.claude/settings.local.json +56 -0
  2. lixinger_python-0.1.2/.env.example +21 -0
  3. lixinger_python-0.1.2/.github/copilot-instructions.md +13 -0
  4. lixinger_python-0.1.2/.github/workflows/README.md +141 -0
  5. lixinger_python-0.1.2/.github/workflows/pr-checks.yml +189 -0
  6. lixinger_python-0.1.2/.gitignore +25 -0
  7. lixinger_python-0.1.2/.pre-commit-config.yaml +110 -0
  8. lixinger_python-0.1.2/.python-version +1 -0
  9. lixinger_python-0.1.2/.ruff.toml +121 -0
  10. lixinger_python-0.1.2/ADD_NEW_API_QUICK.md +150 -0
  11. lixinger_python-0.1.2/AGENTS.md +331 -0
  12. lixinger_python-0.1.2/CHANGELOG.md +48 -0
  13. lixinger_python-0.1.2/CLAUDE.md +256 -0
  14. lixinger_python-0.1.2/DOCS.md +334 -0
  15. lixinger_python-0.1.2/LICENSE +21 -0
  16. lixinger_python-0.1.2/MANIFEST.in +9 -0
  17. lixinger_python-0.1.2/PKG-INFO +171 -0
  18. lixinger_python-0.1.2/PUBLISHING.md +246 -0
  19. lixinger_python-0.1.2/README.md +145 -0
  20. lixinger_python-0.1.2/SETUP.md +273 -0
  21. lixinger_python-0.1.2/codecov.yml +104 -0
  22. lixinger_python-0.1.2/docs/api/client.md +7 -0
  23. lixinger_python-0.1.2/docs/api/company/announcement.md +36 -0
  24. lixinger_python-0.1.2/docs/api/company/candlestick.md +100 -0
  25. lixinger_python-0.1.2/docs/api/company/company.md +90 -0
  26. lixinger_python-0.1.2/docs/api/company/dividend.md +36 -0
  27. lixinger_python-0.1.2/docs/api/company/equity_change.md +132 -0
  28. lixinger_python-0.1.2/docs/api/company/fs/non_financial.md +37 -0
  29. lixinger_python-0.1.2/docs/api/company/fundamental.md +93 -0
  30. lixinger_python-0.1.2/docs/api/company/indices.md +34 -0
  31. lixinger_python-0.1.2/docs/api/company/profile.md +91 -0
  32. lixinger_python-0.1.2/docs/api/fund/announcement.md +36 -0
  33. lixinger_python-0.1.2/docs/api/fund/asset_combination.md +36 -0
  34. lixinger_python-0.1.2/docs/api/fund/asset_industry_combination.md +36 -0
  35. lixinger_python-0.1.2/docs/api/fund/candlestick.md +37 -0
  36. lixinger_python-0.1.2/docs/api/fund/fund.md +34 -0
  37. lixinger_python-0.1.2/docs/api/fund/profile.md +34 -0
  38. lixinger_python-0.1.2/docs/api/fund/shareholdings.md +36 -0
  39. lixinger_python-0.1.2/docs/api/index/candlestick.md +37 -0
  40. lixinger_python-0.1.2/docs/api/index/constituent_weightings.md +36 -0
  41. lixinger_python-0.1.2/docs/api/index/constituents.md +35 -0
  42. lixinger_python-0.1.2/docs/api/index/drawdown.md +38 -0
  43. lixinger_python-0.1.2/docs/api/index/fundamental.md +37 -0
  44. lixinger_python-0.1.2/docs/api/index/index.md +34 -0
  45. lixinger_python-0.1.2/docs/api/index/tracking_fund.md +34 -0
  46. lixinger_python-0.1.2/docs/api/overview.md +142 -0
  47. lixinger_python-0.1.2/docs/development/code-style.md +5 -0
  48. lixinger_python-0.1.2/docs/development/contributing.md +5 -0
  49. lixinger_python-0.1.2/docs/development/testing.md +5 -0
  50. lixinger_python-0.1.2/docs/examples/company.md +24 -0
  51. lixinger_python-0.1.2/docs/examples/fund.md +5 -0
  52. lixinger_python-0.1.2/docs/examples/index.md +5 -0
  53. lixinger_python-0.1.2/docs/getting-started/configuration.md +362 -0
  54. lixinger_python-0.1.2/docs/getting-started/installation.md +138 -0
  55. lixinger_python-0.1.2/docs/getting-started/quickstart.md +256 -0
  56. lixinger_python-0.1.2/docs/index.md +109 -0
  57. lixinger_python-0.1.2/examples/company_example.py +39 -0
  58. lixinger_python-0.1.2/examples/company_profile_example.py +32 -0
  59. lixinger_python-0.1.2/fund_response.json +1 -0
  60. lixinger_python-0.1.2/lixinger/__init__.py +79 -0
  61. lixinger_python-0.1.2/lixinger/api/__init__.py +0 -0
  62. lixinger_python-0.1.2/lixinger/api/base.py +94 -0
  63. lixinger_python-0.1.2/lixinger/api/cn/__init__.py +0 -0
  64. lixinger_python-0.1.2/lixinger/api/cn/company/__init__.py +34 -0
  65. lixinger_python-0.1.2/lixinger/api/cn/company/announcement.py +74 -0
  66. lixinger_python-0.1.2/lixinger/api/cn/company/candlestick.py +76 -0
  67. lixinger_python-0.1.2/lixinger/api/cn/company/company.py +88 -0
  68. lixinger_python-0.1.2/lixinger/api/cn/company/dividend.py +67 -0
  69. lixinger_python-0.1.2/lixinger/api/cn/company/equity_change.py +84 -0
  70. lixinger_python-0.1.2/lixinger/api/cn/company/fs/__init__.py +5 -0
  71. lixinger_python-0.1.2/lixinger/api/cn/company/fs/non_financial.py +77 -0
  72. lixinger_python-0.1.2/lixinger/api/cn/company/fundamental.py +228 -0
  73. lixinger_python-0.1.2/lixinger/api/cn/company/indices.py +52 -0
  74. lixinger_python-0.1.2/lixinger/api/cn/company/namespace.py +66 -0
  75. lixinger_python-0.1.2/lixinger/api/cn/company/profile.py +66 -0
  76. lixinger_python-0.1.2/lixinger/api/cn/fund/__init__.py +35 -0
  77. lixinger_python-0.1.2/lixinger/api/cn/fund/announcement.py +89 -0
  78. lixinger_python-0.1.2/lixinger/api/cn/fund/asset_combination.py +82 -0
  79. lixinger_python-0.1.2/lixinger/api/cn/fund/asset_industry_combination.py +93 -0
  80. lixinger_python-0.1.2/lixinger/api/cn/fund/candlestick.py +84 -0
  81. lixinger_python-0.1.2/lixinger/api/cn/fund/fund.py +70 -0
  82. lixinger_python-0.1.2/lixinger/api/cn/fund/profile.py +60 -0
  83. lixinger_python-0.1.2/lixinger/api/cn/fund/shareholdings.py +86 -0
  84. lixinger_python-0.1.2/lixinger/api/cn/index/__init__.py +32 -0
  85. lixinger_python-0.1.2/lixinger/api/cn/index/candlestick.py +86 -0
  86. lixinger_python-0.1.2/lixinger/api/cn/index/constituent_weightings.py +71 -0
  87. lixinger_python-0.1.2/lixinger/api/cn/index/constituents.py +71 -0
  88. lixinger_python-0.1.2/lixinger/api/cn/index/drawdown.py +87 -0
  89. lixinger_python-0.1.2/lixinger/api/cn/index/fundamental.py +87 -0
  90. lixinger_python-0.1.2/lixinger/api/cn/index/index.py +69 -0
  91. lixinger_python-0.1.2/lixinger/api/cn/index/tracking_fund.py +71 -0
  92. lixinger_python-0.1.2/lixinger/client.py +184 -0
  93. lixinger_python-0.1.2/lixinger/config.py +72 -0
  94. lixinger_python-0.1.2/lixinger/exceptions.py +22 -0
  95. lixinger_python-0.1.2/lixinger/models/__init__.py +5 -0
  96. lixinger_python-0.1.2/lixinger/models/cn/__init__.py +1 -0
  97. lixinger_python-0.1.2/lixinger/models/cn/company/__init__.py +20 -0
  98. lixinger_python-0.1.2/lixinger/models/cn/company/announcement.py +20 -0
  99. lixinger_python-0.1.2/lixinger/models/cn/company/candlestick.py +24 -0
  100. lixinger_python-0.1.2/lixinger/models/cn/company/company.py +33 -0
  101. lixinger_python-0.1.2/lixinger/models/cn/company/dividend.py +25 -0
  102. lixinger_python-0.1.2/lixinger/models/cn/company/equity_change.py +26 -0
  103. lixinger_python-0.1.2/lixinger/models/cn/company/fs/__init__.py +5 -0
  104. lixinger_python-0.1.2/lixinger/models/cn/company/fs/non_financial.py +20 -0
  105. lixinger_python-0.1.2/lixinger/models/cn/company/fundamental.py +16 -0
  106. lixinger_python-0.1.2/lixinger/models/cn/company/indices.py +17 -0
  107. lixinger_python-0.1.2/lixinger/models/cn/fund/__init__.py +19 -0
  108. lixinger_python-0.1.2/lixinger/models/cn/fund/announcement.py +21 -0
  109. lixinger_python-0.1.2/lixinger/models/cn/fund/asset_combination.py +40 -0
  110. lixinger_python-0.1.2/lixinger/models/cn/fund/asset_industry_combination.py +24 -0
  111. lixinger_python-0.1.2/lixinger/models/cn/fund/candlestick.py +23 -0
  112. lixinger_python-0.1.2/lixinger/models/cn/fund/fund.py +23 -0
  113. lixinger_python-0.1.2/lixinger/models/cn/fund/profile.py +28 -0
  114. lixinger_python-0.1.2/lixinger/models/cn/fund/shareholdings.py +21 -0
  115. lixinger_python-0.1.2/lixinger/models/cn/index/__init__.py +19 -0
  116. lixinger_python-0.1.2/lixinger/models/cn/index/candlestick.py +22 -0
  117. lixinger_python-0.1.2/lixinger/models/cn/index/constituent_weightings.py +16 -0
  118. lixinger_python-0.1.2/lixinger/models/cn/index/constituents.py +17 -0
  119. lixinger_python-0.1.2/lixinger/models/cn/index/drawdown.py +16 -0
  120. lixinger_python-0.1.2/lixinger/models/cn/index/fundamental.py +16 -0
  121. lixinger_python-0.1.2/lixinger/models/cn/index/index.py +24 -0
  122. lixinger_python-0.1.2/lixinger/models/cn/index/tracking_fund.py +19 -0
  123. lixinger_python-0.1.2/lixinger/py.typed +0 -0
  124. lixinger_python-0.1.2/lixinger/utils/__init__.py +0 -0
  125. lixinger_python-0.1.2/lixinger/utils/api.py +15 -0
  126. lixinger_python-0.1.2/lixinger/utils/dataframe.py +57 -0
  127. lixinger_python-0.1.2/lixinger/utils/dict.py +22 -0
  128. lixinger_python-0.1.2/lixinger/utils/rate_limiter.py +35 -0
  129. lixinger_python-0.1.2/lixinger/utils/retry.py +31 -0
  130. lixinger_python-0.1.2/mkdocs.yml +149 -0
  131. lixinger_python-0.1.2/mypy.ini +5 -0
  132. lixinger_python-0.1.2/pyproject.toml +129 -0
  133. lixinger_python-0.1.2/scripts/explore_api.py +118 -0
  134. lixinger_python-0.1.2/scripts/generate_docs.py +492 -0
  135. lixinger_python-0.1.2/scripts/publish.sh +56 -0
  136. lixinger_python-0.1.2/tests/api/cn/company/test_announcement.py +77 -0
  137. lixinger_python-0.1.2/tests/api/cn/company/test_dividend.py +57 -0
  138. lixinger_python-0.1.2/tests/conftest.py +12 -0
  139. lixinger_python-0.1.2/tests/test_candlestick.py +121 -0
  140. lixinger_python-0.1.2/tests/test_client.py +46 -0
  141. lixinger_python-0.1.2/tests/test_cn_index_candlestick.py +116 -0
  142. lixinger_python-0.1.2/tests/test_cn_index_constituent_weightings.py +62 -0
  143. lixinger_python-0.1.2/tests/test_company.py +73 -0
  144. lixinger_python-0.1.2/tests/test_company_profile.py +77 -0
  145. lixinger_python-0.1.2/tests/test_equity_change.py +114 -0
  146. lixinger_python-0.1.2/tests/test_fund.py +78 -0
  147. lixinger_python-0.1.2/tests/test_fund_announcement.py +91 -0
  148. lixinger_python-0.1.2/tests/test_fund_asset_combination.py +150 -0
  149. lixinger_python-0.1.2/tests/test_fund_asset_industry_combination.py +75 -0
  150. lixinger_python-0.1.2/tests/test_fund_candlestick.py +81 -0
  151. lixinger_python-0.1.2/tests/test_fund_profile.py +89 -0
  152. lixinger_python-0.1.2/tests/test_fund_shareholdings.py +89 -0
  153. lixinger_python-0.1.2/tests/test_fundamental.py +82 -0
  154. lixinger_python-0.1.2/tests/test_index.py +80 -0
  155. lixinger_python-0.1.2/tests/test_index_constituents.py +90 -0
  156. lixinger_python-0.1.2/tests/test_index_drawdown.py +53 -0
  157. lixinger_python-0.1.2/tests/test_index_fundamental.py +99 -0
  158. lixinger_python-0.1.2/tests/test_indices.py +59 -0
  159. lixinger_python-0.1.2/tests/test_integration.py +634 -0
  160. lixinger_python-0.1.2/tests/test_non_financial_statements.py +110 -0
  161. lixinger_python-0.1.2/tests/test_tracking_fund.py +85 -0
  162. lixinger_python-0.1.2/uv.lock +1863 -0
@@ -0,0 +1,56 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(tree -L 3 -I \".venv|venv|__pycache__|*.pyc\")",
5
+ "Bash(find . -type d -not -path ./.venv/* -not -path ./.git/*)",
6
+ "Bash(uv --version)",
7
+ "WebFetch(domain:www.lixinger.com)",
8
+ "Bash(uv add python-dotenv)",
9
+ "Bash(python -c \"from lixinger.config import get_config_from_env; print\\(''Config module loads successfully''\\)\")",
10
+ "Bash(python -c \"from lixinger import LixingerClient; print\\(''LixingerClient imports successfully''\\)\")",
11
+ "Bash(ls -la .env*)",
12
+ "Bash(python -c \"\nimport sys\nsys.path.insert\\(0, ''.''\\)\n\n# Test that api_key parameter doesn''t exist\nfrom lixinger.client import LixingerClient\nimport inspect\n\nsig = inspect.signature\\(LixingerClient.__init__\\)\nparams = list\\(sig.parameters.keys\\(\\)\\)\nprint\\(''LixingerClient.__init__ parameters:'', params\\)\nprint\\(\\)\nprint\\(''✓ api_key parameter removed!'' if ''api_key'' not in params else ''✗ api_key still exists!''\\)\nprint\\(''✓ Only keyword-only parameters remain''\\)\n\")",
13
+ "Bash(uv add --dev pre-commit)",
14
+ "Bash(uv pip install -e .)",
15
+ "Bash(python -c \"import lixinger; print\\(''✓ lixinger imports successfully''\\)\")",
16
+ "Bash(python -c \"from lixinger import LixingerClient; print\\(''✓ LixingerClient imports successfully''\\)\")",
17
+ "Bash(python -c \"import yaml; yaml.safe_load\\(open\\(''codecov.yml''\\)\\)\")",
18
+ "Bash(python3 -c \"\nimport yaml\n\nwith open\\(''codecov.yml'', ''r''\\) as f:\n config = yaml.safe_load\\(f\\)\n\nprint\\(''✅ Codecov Configuration Validation\\\\n''\\)\nprint\\(f''Project Coverage Target: {config[\"\"coverage\"\"][\"\"status\"\"][\"\"project\"\"][\"\"default\"\"][\"\"target\"\"]}''\\)\nprint\\(f''Project Threshold: {config[\"\"coverage\"\"][\"\"status\"\"][\"\"project\"\"][\"\"default\"\"][\"\"threshold\"\"]}''\\)\nprint\\(f''Patch Coverage Target: {config[\"\"coverage\"\"][\"\"status\"\"][\"\"patch\"\"][\"\"default\"\"][\"\"target\"\"]}''\\)\nprint\\(f''Patch Threshold: {config[\"\"coverage\"\"][\"\"status\"\"][\"\"patch\"\"][\"\"default\"\"][\"\"threshold\"\"]}''\\)\nprint\\(f''\\\\nFlags Configured: {list\\(config[\"\"flags\"\"].keys\\(\\)\\)}''\\)\nprint\\(f''Components: {len\\(config[\"\"component_management\"\"][\"\"individual_components\"\"]\\)} defined''\\)\nprint\\(f''GitHub Annotations: {config[\"\"github_checks\"\"][\"\"annotations\"\"]}''\\)\nprint\\(f''\\\\n✅ All fields are valid!''\\)\n\")",
19
+ "Bash(curl --data-binary @codecov.yml https://codecov.io/validate)",
20
+ "WebFetch(domain:docs.codecov.com)",
21
+ "WebSearch",
22
+ "Bash(python3 -c \"import yaml; yaml.safe_load\\(open\\(''''codecov.yml''''\\)\\)\")",
23
+ "Bash(ls -1 codecov.yml docs/CODECOV*.md)",
24
+ "Bash(curl -s --data-binary @codecov.yml https://codecov.io/validate)",
25
+ "Bash(uv run ruff check /Users/JianGuo/Documents/CursorProject/lixinger-python/AGENTS.md)",
26
+ "Bash(ls -la /Users/JianGuo/Documents/CursorProject/lixinger-python/*.md)",
27
+ "Bash(tree /Users/JianGuo/Documents/CursorProject/lixinger-python/lixinger/api -L 3)",
28
+ "Bash(tree /Users/JianGuo/Documents/CursorProject/lixinger-python/lixinger/models -L 3)",
29
+ "Bash(uv run pytest tests/ -m \"not integration\" -v)",
30
+ "Bash(uv run ruff check lixinger/)",
31
+ "Bash(uv run mypy lixinger/)",
32
+ "Bash(uv run pytest tests/ -m \"not integration\" --tb=short)",
33
+ "Bash(tree /Users/JianGuo/Documents/CursorProject/lixinger-python/lixinger -L 4 -I __pycache__)",
34
+ "Bash(uv run ruff check .)",
35
+ "Bash(find /Users/JianGuo/Documents/CursorProject/lixinger-python/examples -name *.py)",
36
+ "Bash(tree /Users/JianGuo/Documents/CursorProject/lixinger-python/lixinger -L 4 -I '__pycache__|*.pyc' --dirsfirst)",
37
+ "Bash(uv run pytest tests/ -m \"not integration\" --tb=short -q)",
38
+ "Bash(uv run pytest tests/ -m \"not integration\" -q)",
39
+ "Bash(uv add:*)",
40
+ "Bash(uv run python:*)",
41
+ "Bash(uv run mkdocs build:*)",
42
+ "Bash(git check-ignore:*)",
43
+ "Bash(uv run pytest:*)",
44
+ "Bash(uv run ruff check:*)",
45
+ "Bash(uv run ruff format:*)",
46
+ "Bash(lsof:*)",
47
+ "Bash(xargs kill:*)",
48
+ "Bash(chmod +x scripts/publish.sh)",
49
+ "Bash(chmod +x scripts/check_before_publish.py)",
50
+ "Bash(uv run twine check dist/*)",
51
+ "Bash(unzip -l dist/lixinger_python-0.1.0-py3-none-any.whl | head -30)",
52
+ "Bash(find lixinger -type f -name \"*.py\" -exec grep -l \"^import pandera as pa\" {} \\\\;)",
53
+ "Bash(find lixinger -type f -name \"*.py\" -exec sed -i '' 's/^import pandera as pa$/import pandera.pandas as pa/' {} \\\\;)"
54
+ ]
55
+ }
56
+ }
@@ -0,0 +1,21 @@
1
+ # Lixinger API Configuration
2
+ # Copy this file to .env and fill in your actual values
3
+
4
+ # Your Lixinger API key (required)
5
+ # Get your API key from: https://www.lixinger.com/open/api
6
+ LIXINGER_API_KEY=your_api_key_here
7
+
8
+ # Optional: Custom API base URL (defaults to https://open.lixinger.com/api)
9
+ # LIXINGER_BASE_URL=https://open.lixinger.com/api
10
+
11
+ # Optional: Request timeout in seconds (defaults to 30.0)
12
+ # LIXINGER_TIMEOUT=30.0
13
+
14
+ # Optional: Maximum number of retry attempts (defaults to 3)
15
+ # LIXINGER_MAX_RETRIES=3
16
+
17
+ # Optional: Proxy URL (supports SOCKS)
18
+ # LIXINGER_PROXY=socks5://localhost:1080
19
+
20
+ # Optional: Maximum requests per minute (defaults to 1000)
21
+ # LIXINGER_MAX_REQUESTS_PER_MINUTE=1000
@@ -0,0 +1,13 @@
1
+ When performing a code review:
2
+
3
+ 1. Respond in English
4
+ 2. If the PR is adding new API, review the request param and response param is strictly aligning with the API doc provided in PR description.
5
+
6
+ **Checklist:**
7
+ - [ ] All parameter names match API docs exactly
8
+ - [ ] All parameter types match API docs exactly
9
+ - [ ] All response fields match API docs exactly
10
+ - [ ] Optional parameters are correctly marked
11
+ - [ ] Default values match API docs (if specified)
12
+ - [ ] Constraints are validated (enums, ranges, etc.)
13
+ - [ ] Documentation links included in docstrings
@@ -0,0 +1,141 @@
1
+ # GitHub Actions Workflows
2
+
3
+ This directory contains CI/CD workflows for the lixinger-python project.
4
+
5
+ ## Workflows
6
+
7
+ ### PR Checks (`pr-checks.yml`)
8
+
9
+ Runs on every pull request and push to main branches.
10
+
11
+ **Required Checks (PR Gate):**
12
+
13
+ 1. **Lint Check**
14
+ - Ruff linter (`ruff check .`)
15
+ - Ruff formatter (`ruff format --check .`)
16
+ - Mypy type checker (`mypy lixinger/`)
17
+
18
+ 2. **Test Suite**
19
+ - Unit tests only (integration tests excluded)
20
+ - Runs with `pytest -m "not integration"`
21
+ - Generates coverage report
22
+ - Uploads to Codecov
23
+
24
+ 3. **Compilation Check**
25
+ - Python compilation (`py_compile`)
26
+ - Import validation
27
+ - Ensures package can be imported
28
+
29
+ **Optional Check:**
30
+
31
+ 4. **Integration Tests**
32
+ - Only runs if `LIXINGER_API_KEY` secret is available
33
+ - Skipped for external PRs (normal behavior)
34
+ - Tests with real API (non-blocking)
35
+
36
+ ## Setup
37
+
38
+ ### Required Secrets
39
+
40
+ No secrets required for basic PR checks (lint, test, compile).
41
+
42
+ ### Optional Secrets
43
+
44
+ - `LIXINGER_API_KEY` - For integration tests (optional, only for internal PRs)
45
+
46
+ To add secrets:
47
+ 1. Go to repository Settings → Secrets and variables → Actions
48
+ 2. Add `LIXINGER_API_KEY` with your API key
49
+
50
+ ## Branch Protection Rules
51
+
52
+ Recommended settings for main/master branch:
53
+
54
+ 1. Require status checks to pass before merging:
55
+ - [x] Lint (Ruff & Mypy)
56
+ - [x] Test Suite
57
+ - [x] Compilation Check
58
+ - [x] All Checks Passed
59
+
60
+ 2. Optional (not required):
61
+ - [ ] Integration Tests (Optional)
62
+
63
+ 3. Other recommended settings:
64
+ - [x] Require branches to be up to date before merging
65
+ - [x] Require conversation resolution before merging
66
+ - [ ] Require signed commits (optional)
67
+
68
+ ## Local Testing
69
+
70
+ Run the same checks locally before pushing:
71
+
72
+ ```bash
73
+ # Lint checks
74
+ uv run ruff check .
75
+ uv run ruff format --check .
76
+ uv run mypy lixinger/
77
+
78
+ # Unit tests
79
+ uv run pytest tests/ -m "not integration" -v
80
+
81
+ # Compilation check
82
+ python -m py_compile lixinger/**/*.py
83
+ uv run python -c "import lixinger"
84
+
85
+ # Integration tests (optional, requires API key)
86
+ uv run pytest tests/test_integration.py -v
87
+ ```
88
+
89
+ ## Troubleshooting
90
+
91
+ ### "Ruff check failed"
92
+ - Run `uv run ruff check . --fix` to auto-fix issues
93
+ - Run `uv run ruff format .` to format code
94
+
95
+ ### "Mypy type check failed"
96
+ - Fix type hints in the reported files
97
+ - Ensure all functions have proper type annotations
98
+
99
+ ### "Tests failed"
100
+ - Run `uv run pytest tests/ -v` locally to see details
101
+ - Make sure `.env` is not committed (only `.env.example`)
102
+ - Check that mocks match expected structure
103
+
104
+ ### "Compilation failed"
105
+ - Check for syntax errors in Python files
106
+ - Ensure all imports are correct
107
+
108
+ ### "Integration tests skipped"
109
+ - Normal for external PRs (no API key available)
110
+ - Internal PRs need `LIXINGER_API_KEY` secret configured
111
+
112
+ ## CI/CD Best Practices
113
+
114
+ 1. **Fast Feedback**
115
+ - Lint checks run first (fastest)
116
+ - Tests run in parallel with compilation
117
+ - Integration tests are optional (non-blocking)
118
+
119
+ 2. **Resource Efficiency**
120
+ - Uses uv caching for faster installs
121
+ - Only runs integration tests when necessary
122
+ - Parallel job execution
123
+
124
+ 3. **Security**
125
+ - API keys stored as secrets
126
+ - External PRs cannot access secrets
127
+ - Integration tests continue on error
128
+
129
+ 4. **Developer Experience**
130
+ - Clear job names
131
+ - Helpful error messages
132
+ - Same commands work locally
133
+
134
+ ## Adding New Checks
135
+
136
+ To add a new check to the PR gate:
137
+
138
+ 1. Add a new job in `pr-checks.yml`
139
+ 2. Add the job name to `needs:` in the `all-checks` job
140
+ 3. Update branch protection rules to require the new check
141
+ 4. Document the check in this README
@@ -0,0 +1,189 @@
1
+ name: PR Checks
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - main
7
+ push:
8
+ branches:
9
+ - main
10
+
11
+ jobs:
12
+ lint:
13
+ name: Lint (Ruff & Mypy)
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Checkout code
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v4
21
+ with:
22
+ enable-cache: true
23
+
24
+ - name: Set up Python
25
+ uses: actions/setup-python@v5
26
+ with:
27
+ python-version-file: ".python-version"
28
+
29
+ - name: Install dependencies
30
+ run: |
31
+ uv sync --all-groups
32
+ uv pip install -e .
33
+
34
+ - name: Run Ruff linter
35
+ run: uv run ruff check .
36
+
37
+ - name: Run Ruff formatter check
38
+ run: uv run ruff format --check .
39
+
40
+ - name: Run Mypy type checker
41
+ run: uv run mypy lixinger/
42
+
43
+ test:
44
+ name: Test Suite
45
+ runs-on: ubuntu-latest
46
+ strategy:
47
+ matrix:
48
+ python-version: ["3.13"]
49
+ steps:
50
+ - name: Checkout code
51
+ uses: actions/checkout@v4
52
+
53
+ - name: Install uv
54
+ uses: astral-sh/setup-uv@v4
55
+ with:
56
+ enable-cache: true
57
+
58
+ - name: Set up Python ${{ matrix.python-version }}
59
+ uses: actions/setup-python@v5
60
+ with:
61
+ python-version: ${{ matrix.python-version }}
62
+
63
+ - name: Install dependencies
64
+ run: |
65
+ uv sync --all-groups
66
+ uv pip install -e .
67
+
68
+ - name: Run unit tests (exclude integration tests)
69
+ env:
70
+ LIXINGER_API_KEY: ${{ secrets.LIXINGER_API_KEY }}
71
+ run: uv run pytest -m "not integration" -v --cov=lixinger --cov-report=xml --cov-report=term --cov-branch --junitxml=junit.xml -o junit_family=legacy -s
72
+
73
+ - name: Upload coverage reports to Codecov
74
+ uses: codecov/codecov-action@v5
75
+ with:
76
+ token: ${{ secrets.CODECOV_TOKEN }}
77
+ flags: unit
78
+ files: ./coverage.xml
79
+ fail_ci_if_error: false
80
+ verbose: true
81
+
82
+ - name: Upload test results to Codecov
83
+ if: ${{ !cancelled() }}
84
+ uses: codecov/codecov-action@v5
85
+ with:
86
+ token: ${{ secrets.CODECOV_TOKEN }}
87
+ report_type: test_results
88
+ fail_ci_if_error: false
89
+ compile:
90
+ name: Compilation Check
91
+ runs-on: ubuntu-latest
92
+ steps:
93
+ - name: Checkout code
94
+ uses: actions/checkout@v4
95
+
96
+ - name: Install uv
97
+ uses: astral-sh/setup-uv@v4
98
+ with:
99
+ enable-cache: true
100
+
101
+ - name: Set up Python
102
+ uses: actions/setup-python@v5
103
+ with:
104
+ python-version-file: ".python-version"
105
+
106
+ - name: Install dependencies
107
+ run: uv sync
108
+
109
+ - name: Check Python compilation
110
+ run: |
111
+ python -m py_compile lixinger/**/*.py
112
+ echo "✓ All Python files compile successfully"
113
+
114
+ - name: Check imports
115
+ run: |
116
+ uv run python -c "import lixinger; print('✓ Package imports successfully')"
117
+ uv run python -c "from lixinger import LixingerClient; print('✓ LixingerClient imports successfully')"
118
+
119
+ integration-test:
120
+ name: Integration Tests (Optional)
121
+ runs-on: ubuntu-latest
122
+ # Only run if API key secret is available
123
+ if: github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name
124
+ steps:
125
+ - name: Checkout code
126
+ uses: actions/checkout@v4
127
+
128
+ - name: Install uv
129
+ uses: astral-sh/setup-uv@v4
130
+ with:
131
+ enable-cache: true
132
+
133
+ - name: Set up Python
134
+ uses: actions/setup-python@v5
135
+ with:
136
+ python-version-file: ".python-version"
137
+
138
+ - name: Install dependencies
139
+ run: |
140
+ uv sync --all-groups
141
+ uv pip install -e .
142
+
143
+ - name: Run integration tests
144
+ id: integration_tests
145
+ env:
146
+ LIXINGER_API_KEY: ${{ secrets.LIXINGER_API_KEY }}
147
+ run: |
148
+ if [ -n "$LIXINGER_API_KEY" ]; then
149
+ echo "Running integration tests with real API..."
150
+ uv run pytest tests/test_integration.py -v --cov=lixinger --cov-report=xml --cov-branch -s
151
+ echo "ran=true" >> $GITHUB_OUTPUT
152
+ else
153
+ echo "⚠️ Skipping integration tests (no API key available)"
154
+ echo "This is normal for external PRs"
155
+ echo "ran=false" >> $GITHUB_OUTPUT
156
+ fi
157
+ continue-on-error: true
158
+
159
+ - name: Upload integration coverage to Codecov
160
+ if: steps.integration_tests.outputs.ran == 'true'
161
+ uses: codecov/codecov-action@v5
162
+ with:
163
+ token: ${{ secrets.CODECOV_TOKEN }}
164
+ flags: integration
165
+ files: ./coverage.xml
166
+ fail_ci_if_error: false
167
+ verbose: true
168
+
169
+ all-checks:
170
+ name: All Checks Passed
171
+ runs-on: ubuntu-latest
172
+ needs: [lint, test, compile]
173
+ if: always()
174
+ steps:
175
+ - name: Check all job status
176
+ run: |
177
+ if [ "${{ needs.lint.result }}" != "success" ]; then
178
+ echo "❌ Lint checks failed"
179
+ exit 1
180
+ fi
181
+ if [ "${{ needs.test.result }}" != "success" ]; then
182
+ echo "❌ Tests failed"
183
+ exit 1
184
+ fi
185
+ if [ "${{ needs.compile.result }}" != "success" ]; then
186
+ echo "❌ Compilation checks failed"
187
+ exit 1
188
+ fi
189
+ echo "✅ All required checks passed!"
@@ -0,0 +1,25 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+ *.egg
9
+ .env
10
+
11
+ # PyPI credentials
12
+ .pypirc
13
+
14
+ # Virtual environments
15
+ .venv
16
+ .mypy_cache/
17
+ .pytest_cache/
18
+ .ruff_cache/
19
+ __pycache__/
20
+ .coverage
21
+ coverage.xml
22
+ junit.xml
23
+
24
+ # MkDocs generated site
25
+ site/
@@ -0,0 +1,110 @@
1
+ # Pre-commit hooks configuration
2
+ # See https://pre-commit.com for more information
3
+
4
+ default_install_hook_types: [pre-commit, commit-msg]
5
+ default_stages: [pre-commit]
6
+
7
+ repos:
8
+ # Ruff - Fast Python linter and formatter
9
+ - repo: https://github.com/astral-sh/ruff-pre-commit
10
+ rev: v0.11.10
11
+ hooks:
12
+ # Run the linter
13
+ - id: ruff
14
+ args: [--fix]
15
+ name: Ruff linter (auto-fix)
16
+ # Run the formatter
17
+ - id: ruff-format
18
+ name: Ruff formatter
19
+
20
+ # Mypy - Static type checker
21
+ - repo: https://github.com/pre-commit/mirrors-mypy
22
+ rev: v1.15.0
23
+ hooks:
24
+ - id: mypy
25
+ name: Mypy type checker
26
+ additional_dependencies:
27
+ - types-python-dateutil
28
+ - pydantic>=2.0.0
29
+ args: [--config-file=pyproject.toml]
30
+ files: ^lixinger/
31
+
32
+ # Pytest - Run fast unit tests only (not integration tests)
33
+ - repo: local
34
+ hooks:
35
+ - id: pytest-fast
36
+ name: Pytest (unit tests only)
37
+ entry: uv run pytest
38
+ args: [tests/, -m, "not integration", --tb=short, -q]
39
+ language: system
40
+ pass_filenames: false
41
+ always_run: true
42
+ stages: [pre-commit]
43
+
44
+ # Standard pre-commit hooks
45
+ - repo: https://github.com/pre-commit/pre-commit-hooks
46
+ rev: v5.0.0
47
+ hooks:
48
+ # Check for files that would conflict in case-insensitive filesystems
49
+ - id: check-case-conflict
50
+ name: Check case conflicts
51
+
52
+ # Check for merge conflicts
53
+ - id: check-merge-conflict
54
+ name: Check merge conflicts
55
+
56
+ # Check YAML files
57
+ - id: check-yaml
58
+ name: Check YAML syntax
59
+
60
+ # Check TOML files
61
+ - id: check-toml
62
+ name: Check TOML syntax
63
+
64
+ # Check JSON files
65
+ - id: check-json
66
+ name: Check JSON syntax
67
+
68
+ # Ensure files end with newline
69
+ - id: end-of-file-fixer
70
+ name: Fix end of files
71
+ exclude: \.lock$
72
+
73
+ # Trim trailing whitespace
74
+ - id: trailing-whitespace
75
+ name: Trim trailing whitespace
76
+ exclude: \.lock$
77
+
78
+ # Check for debug statements
79
+ - id: debug-statements
80
+ name: Check debug statements
81
+
82
+ # Check for private key files
83
+ - id: detect-private-key
84
+ name: Detect private keys
85
+
86
+ # Check for large files
87
+ - id: check-added-large-files
88
+ name: Check large files
89
+ args: [--maxkb=500]
90
+
91
+ # dotenv-linter - Check .env files
92
+ - repo: https://github.com/wemake-services/dotenv-linter
93
+ rev: 0.7.0
94
+ hooks:
95
+ - id: dotenv-linter
96
+ name: Lint .env files
97
+ files: ^\.env\.example$
98
+
99
+ # CI configuration - disable some checks in CI
100
+ ci:
101
+ autofix_commit_msg: |
102
+ [pre-commit.ci] auto fixes from pre-commit.com hooks
103
+
104
+ for more information, see https://pre-commit.ci
105
+ autofix_prs: true
106
+ autoupdate_branch: ""
107
+ autoupdate_commit_msg: "[pre-commit.ci] pre-commit autoupdate"
108
+ autoupdate_schedule: weekly
109
+ skip: [pytest-fast] # Skip pytest in pre-commit.ci (run in GitHub Actions)
110
+ submodules: false
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,121 @@
1
+ line-length = 120
2
+
3
+ [format]
4
+ quote-style = "double"
5
+ indent-style = "space"
6
+ skip-magic-trailing-comma = false
7
+
8
+ [lint]
9
+ preview = false
10
+ select = [
11
+ # Pyflakes (F) - catch common errors
12
+ "F",
13
+ # Pycodestyle (E, W) - PEP 8 style guide
14
+ "E",
15
+ "W",
16
+ # isort (I) - import sorting
17
+ "I",
18
+ # pep8-naming (N) - naming conventions
19
+ "N",
20
+ # pydocstyle (D) - docstring conventions
21
+ "D",
22
+ # pyupgrade (UP) - upgrade syntax for newer Python versions
23
+ "UP",
24
+ # flake8-bugbear (B) - find bugs and design problems
25
+ "B",
26
+ # flake8-comprehensions (C4) - better comprehensions
27
+ "C4",
28
+ # flake8-pie (PIE) - remove code smells
29
+ "PIE",
30
+ # flake8-return (RET) - return statement best practices
31
+ "RET",
32
+ # flake8-simplify (SIM) - simplify code
33
+ "SIM",
34
+ # flake8-unused-arguments (ARG) - unused arguments
35
+ "ARG",
36
+ # flake8-use-pathlib (PTH) - use pathlib
37
+ "PTH",
38
+ # eradicate (ERA) - commented-out code
39
+ "ERA",
40
+ # pandas-vet (PD) - pandas best practices
41
+ "PD",
42
+ # pygrep-hooks (PGH) - additional checks
43
+ "PGH",
44
+ # Pylint (PL) - additional checks
45
+ "PL",
46
+ # tryceratops (TRY) - exception handling
47
+ "TRY",
48
+ # flake8-type-checking (TCH) - type checking imports
49
+ "TCH",
50
+ # flake8-future-annotations (FA) - future annotations
51
+ "FA",
52
+ ]
53
+
54
+ ignore = [
55
+ # Allow non-abstract empty methods in base classes
56
+ "B027",
57
+ # Allow boolean positional values in function calls
58
+ "FBT",
59
+ # Ignore specific pydocstyle errors
60
+ "D100", # Missing docstring in public module
61
+ "D101", # Missing docstring in public class
62
+ "D102", # Missing docstring in public method
63
+ "D103", # Missing docstring in public function
64
+ "D104", # Missing docstring in public package
65
+ "D105", # Missing docstring in magic method
66
+ "D107", # Missing docstring in __init__
67
+ # Allow relative imports
68
+ "TID252",
69
+ # Allow print statements (useful for debugging)
70
+ "T201",
71
+ # Allow assert statements in tests
72
+ "S101",
73
+ # Allow magic numbers
74
+ "PLR2004",
75
+ # Allow long lines in URLs/imports
76
+ "E501",
77
+ # Allow unused variables in comprehensions
78
+ "F841",
79
+ # Allow try-except-pass
80
+ "S110",
81
+ "B904",
82
+ "TRY003",
83
+ "TRY201",
84
+ "PGH003",
85
+ "ARG001",
86
+ "TRY300",
87
+ "PD901",
88
+ "D404",
89
+ "D415",
90
+ "D400",
91
+ "PLR0915"
92
+ ]
93
+
94
+ [lint.per-file-ignores]
95
+ # Tests can have longer lines, unused imports, and assertions
96
+ "tests/**/*.py" = [
97
+ "E501", # Line too long
98
+ "F401", # Unused imports
99
+ "S101", # Use of assert
100
+ "ARG", # Unused arguments
101
+ "PLR2004", # Magic numbers
102
+ ]
103
+ "data_provider/**/*.py" = [
104
+ "ERA001", # Remove commented-out code
105
+ ]
106
+ # Allow unused imports in __init__.py files
107
+ "**/__init__.py" = [
108
+ "F401", # Unused imports
109
+ ]
110
+ # Allow Flask app patterns
111
+ "app.py" = [
112
+ "F401", # Unused imports (Flask decorators)
113
+ ]
114
+
115
+ [lint.isort]
116
+ known-first-party = ["analyzer", "core", "data_provider"]
117
+ force-sort-within-sections = true
118
+ split-on-trailing-comma = true
119
+
120
+ [lint.mccabe]
121
+ max-complexity = 10