electiondata-my-mcp 0.1.0__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.
- electiondata_my_mcp-0.1.0/.cursor/rules/ponytail.mdc +30 -0
- electiondata_my_mcp-0.1.0/.cursor/rules/pytest.mdc +337 -0
- electiondata_my_mcp-0.1.0/.cursor/rules/uv.mdc +29 -0
- electiondata_my_mcp-0.1.0/.cursor/skills/query-electiondatamy-api/SKILL.md +91 -0
- electiondata_my_mcp-0.1.0/.cursor/skills/query-electiondatamy-api/reference.md +164 -0
- electiondata_my_mcp-0.1.0/.env.example +1 -0
- electiondata_my_mcp-0.1.0/.github/workflows/publish.yml +88 -0
- electiondata_my_mcp-0.1.0/.gitignore +34 -0
- electiondata_my_mcp-0.1.0/CHANGELOG.md +0 -0
- electiondata_my_mcp-0.1.0/PKG-INFO +253 -0
- electiondata_my_mcp-0.1.0/README.md +238 -0
- electiondata_my_mcp-0.1.0/RESOURCES.md +13 -0
- electiondata_my_mcp-0.1.0/pyproject.toml +71 -0
- electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/__init__.py +3 -0
- electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/__main__.py +7 -0
- electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/duckdb_lake.py +164 -0
- electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/prompt_loader.py +135 -0
- electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/prompts/query-builder-prompt.md +684 -0
- electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/query_validator.py +128 -0
- electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/server.py +202 -0
- electiondata_my_mcp-0.1.0/tests/conftest.py +34 -0
- electiondata_my_mcp-0.1.0/tests/test_duckdb_lake.py +188 -0
- electiondata_my_mcp-0.1.0/tests/test_main.py +32 -0
- electiondata_my_mcp-0.1.0/tests/test_prompt_loader.py +120 -0
- electiondata_my_mcp-0.1.0/tests/test_query_validator.py +93 -0
- electiondata_my_mcp-0.1.0/tests/test_server.py +204 -0
- electiondata_my_mcp-0.1.0/uv.lock +1223 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Ponytail, lazy senior dev mode
|
|
2
|
+
|
|
3
|
+
You are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written.
|
|
4
|
+
|
|
5
|
+
Before writing any code, stop at the first rung that holds:
|
|
6
|
+
|
|
7
|
+
1. Does this need to be built at all? (YAGNI)
|
|
8
|
+
2. Does it already exist in this codebase? Reuse the helper, util, or pattern that's already here, don't re-write it.
|
|
9
|
+
3. Does the standard library already do this? Use it.
|
|
10
|
+
4. Does a native platform feature cover it? Use it.
|
|
11
|
+
5. Does an already-installed dependency solve it? Use it.
|
|
12
|
+
6. Can this be one line? Make it one line.
|
|
13
|
+
7. Only then: write the minimum code that works.
|
|
14
|
+
|
|
15
|
+
The ladder runs after you understand the problem, not instead of it: read the task and the code it touches, trace the real flow end to end, then climb.
|
|
16
|
+
|
|
17
|
+
Bug fix = root cause, not symptom: a report names a symptom. Grep every caller of the function you touch and fix the shared function once — one guard there is a smaller diff than one per caller, and patching only the path the ticket names leaves a sibling caller still broken.
|
|
18
|
+
|
|
19
|
+
Rules:
|
|
20
|
+
|
|
21
|
+
- No abstractions that weren't explicitly requested.
|
|
22
|
+
- No new dependency if it can be avoided.
|
|
23
|
+
- No boilerplate nobody asked for.
|
|
24
|
+
- Deletion over addition. Boring over clever. Fewest files possible.
|
|
25
|
+
- Shortest working diff wins, but only once you understand the problem. The smallest change in the wrong place isn't lazy, it's a second bug.
|
|
26
|
+
- Question complex requests: "Do you actually need X, or does Y cover it?"
|
|
27
|
+
- Pick the edge-case-correct option when two stdlib approaches are the same size, lazy means less code, not the flimsier algorithm.
|
|
28
|
+
- Mark deliberate simplifications that cut a real corner with a known ceiling (global lock, O(n²) scan, naive heuristic) with a `ponytail:` comment naming the ceiling and upgrade path.
|
|
29
|
+
|
|
30
|
+
Not lazy about: understanding the problem (read it fully and trace the real flow before picking a rung, a small diff you don't understand is just laziness dressed up as efficiency), input validation at trust boundaries, error handling that prevents data loss, security, accessibility, the calibration real hardware needs (the platform is never the spec ideal, a clock drifts, a sensor reads off), anything explicitly requested. Lazy code without its check is unfinished: non-trivial logic leaves ONE runnable check behind, the smallest thing that fails if the logic breaks (an assert-based demo/self-check or one small test file; no frameworks, no fixtures). Trivial one-liners need no test.
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: This rule file outlines comprehensive best practices for using pytest in Python projects, covering code organization, testing strategies, performance optimization, security measures, and common pitfalls to avoid.
|
|
3
|
+
globs: tests/*
|
|
4
|
+
---
|
|
5
|
+
# Pytest Best Practices: A Comprehensive Guide
|
|
6
|
+
|
|
7
|
+
This document provides a detailed guide to using pytest effectively in Python projects, covering various aspects from code organization to security considerations. It aims to provide actionable guidance for developers to improve their testing practices and build robust applications.
|
|
8
|
+
|
|
9
|
+
## Library Information:
|
|
10
|
+
- Name: pytest
|
|
11
|
+
- Tags: development, testing, python
|
|
12
|
+
|
|
13
|
+
## 1. Code Organization and Structure
|
|
14
|
+
|
|
15
|
+
A well-organized codebase is crucial for maintainability and testability. Here are best practices for structuring your pytest projects:
|
|
16
|
+
|
|
17
|
+
### 1.1. Directory Structure
|
|
18
|
+
|
|
19
|
+
- **Separate `tests/` directory:** Keep your tests in a directory separate from your application code, typically named `tests/`. This promotes isolation and cleaner project structure.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
my_project/
|
|
23
|
+
├── my_app/
|
|
24
|
+
│ ├── __init__.py
|
|
25
|
+
│ ├── module1.py
|
|
26
|
+
│ └── module2.py
|
|
27
|
+
├── tests/
|
|
28
|
+
│ ├── __init__.py
|
|
29
|
+
│ ├── test_module1.py
|
|
30
|
+
│ └── test_module2.py
|
|
31
|
+
└── pyproject.toml
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
- **`src` layout (Recommended):** Consider using a `src` layout to further isolate application code from the project root. This prevents import conflicts and improves clarity.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
my_project/
|
|
38
|
+
├── src/
|
|
39
|
+
│ └── my_app/
|
|
40
|
+
│ ├── __init__.py
|
|
41
|
+
│ ├── module1.py
|
|
42
|
+
│ └── module2.py
|
|
43
|
+
├── tests/
|
|
44
|
+
│ ├── __init__.py
|
|
45
|
+
│ ├── test_module1.py
|
|
46
|
+
│ └── test_module2.py
|
|
47
|
+
└── pyproject.toml
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### 1.2. File Naming Conventions
|
|
51
|
+
|
|
52
|
+
- **`test_*.py` or `*_test.py`:** pytest automatically discovers test files matching these patterns.
|
|
53
|
+
- **Descriptive names:** Use clear and descriptive names for your test files to indicate what they are testing (e.g., `test_user_authentication.py`).
|
|
54
|
+
|
|
55
|
+
### 1.3. Module Organization
|
|
56
|
+
|
|
57
|
+
- **Mirror application structure:** Structure your test modules to mirror the structure of your application code. This makes it easier to locate tests for specific modules.
|
|
58
|
+
- **`__init__.py`:** Include `__init__.py` files in your test directories to ensure they are treated as Python packages.
|
|
59
|
+
|
|
60
|
+
### 1.4. Component Architecture
|
|
61
|
+
|
|
62
|
+
- **Isolate components:** Design your application with well-defined components that can be tested independently.
|
|
63
|
+
- **Dependency injection:** Use dependency injection to provide components with their dependencies, making it easier to mock and stub external resources during testing.
|
|
64
|
+
|
|
65
|
+
### 1.5. Code Splitting
|
|
66
|
+
|
|
67
|
+
- **Small, focused functions:** Break down large functions into smaller, focused functions that are easier to test.
|
|
68
|
+
- **Modular design:** Organize your code into modules with clear responsibilities.
|
|
69
|
+
|
|
70
|
+
## 2. Common Patterns and Anti-patterns
|
|
71
|
+
|
|
72
|
+
### 2.1. Design Patterns
|
|
73
|
+
|
|
74
|
+
- **Arrange-Act-Assert (AAA):** Structure your tests following the AAA pattern for clarity.
|
|
75
|
+
- **Arrange:** Set up the test environment and prepare any necessary data.
|
|
76
|
+
- **Act:** Execute the code being tested.
|
|
77
|
+
- **Assert:** Verify that the code behaved as expected.
|
|
78
|
+
|
|
79
|
+
python
|
|
80
|
+
def test_example():
|
|
81
|
+
# Arrange
|
|
82
|
+
data = ...
|
|
83
|
+
expected_result = ...
|
|
84
|
+
|
|
85
|
+
# Act
|
|
86
|
+
result = function_under_test(data)
|
|
87
|
+
|
|
88
|
+
# Assert
|
|
89
|
+
assert result == expected_result
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
- **Fixture factory:** Use fixture factories to create reusable test data.
|
|
93
|
+
|
|
94
|
+
python
|
|
95
|
+
import pytest
|
|
96
|
+
|
|
97
|
+
@pytest.fixture
|
|
98
|
+
def user_factory():
|
|
99
|
+
def create_user(username, email):
|
|
100
|
+
return {"username": username, "email": email}
|
|
101
|
+
return create_user
|
|
102
|
+
|
|
103
|
+
def test_create_user(user_factory):
|
|
104
|
+
user = user_factory("testuser", "test@example.com")
|
|
105
|
+
assert user["username"] == "testuser"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
### 2.2. Recommended Approaches
|
|
109
|
+
|
|
110
|
+
- **Use fixtures for setup and teardown:** Fixtures help manage test dependencies and ensure a clean test environment.
|
|
111
|
+
- **Parameterize tests:** Use `@pytest.mark.parametrize` to run the same test with different inputs and expected outputs, reducing code duplication.
|
|
112
|
+
- **Use descriptive names for tests and fixtures:** This makes it easier to understand the purpose of each test and fixture.
|
|
113
|
+
- **Single Assertion per Test:** A single assertion per test makes it easier to identify the specific failure point.
|
|
114
|
+
|
|
115
|
+
### 2.3. Anti-patterns and Code Smells
|
|
116
|
+
|
|
117
|
+
- **Over-reliance on fixtures:** Avoid creating too many fixtures, especially for simple data. Use direct data definition in the test if it's not reused.
|
|
118
|
+
- **Implicit dependencies:** Make dependencies explicit by passing them as arguments to your functions and tests.
|
|
119
|
+
- **Testing implementation details:** Focus on testing the behavior of your code, not the implementation details. This makes your tests more resilient to refactoring.
|
|
120
|
+
- **Skipping Tests Without a Reason:** Don't skip tests without a valid reason or comment explaining why.
|
|
121
|
+
|
|
122
|
+
### 2.4. State Management
|
|
123
|
+
|
|
124
|
+
- **Stateless tests:** Ensure your tests are stateless and independent to avoid unexpected side effects. Each test should set up its own data and clean up after itself.
|
|
125
|
+
- **Fixture scopes:** Use fixture scopes (`session`, `module`, `function`) to control the lifecycle of fixtures and manage state effectively.
|
|
126
|
+
|
|
127
|
+
### 2.5. Error Handling
|
|
128
|
+
|
|
129
|
+
- **Test exception handling:** Write tests to verify that your code handles exceptions correctly.
|
|
130
|
+
|
|
131
|
+
python
|
|
132
|
+
import pytest
|
|
133
|
+
|
|
134
|
+
def divide(a, b):
|
|
135
|
+
if b == 0:
|
|
136
|
+
raise ValueError("Cannot divide by zero")
|
|
137
|
+
return a / b
|
|
138
|
+
|
|
139
|
+
def test_divide_by_zero():
|
|
140
|
+
with pytest.raises(ValueError) as e:
|
|
141
|
+
divide(10, 0)
|
|
142
|
+
assert str(e.value) == "Cannot divide by zero"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
- **Use `pytest.raises`:** Use `pytest.raises` to assert that a specific exception is raised.
|
|
146
|
+
- **Log errors:** Ensure your application logs errors appropriately, and consider writing tests to verify that errors are logged correctly.
|
|
147
|
+
|
|
148
|
+
## 3. Performance Considerations
|
|
149
|
+
|
|
150
|
+
### 3.1. Optimization Techniques
|
|
151
|
+
|
|
152
|
+
- **Profile slow tests:** Use the `--durations` option to identify slow tests and optimize them.
|
|
153
|
+
- **Parallel test execution:** Use `pytest-xdist` to run tests in parallel and reduce overall test execution time. `pip install pytest-xdist` then run `pytest -n auto`. The `auto` option utilizes all available CPU cores.
|
|
154
|
+
- **Caching:** Cache expensive computations to avoid redundant calculations during testing.
|
|
155
|
+
|
|
156
|
+
### 3.2. Memory Management
|
|
157
|
+
|
|
158
|
+
- **Resource cleanup:** Ensure your tests clean up any resources they allocate, such as temporary files or database connections.
|
|
159
|
+
- **Limit fixture scope:** Use the appropriate fixture scope to minimize the lifetime of fixtures and reduce memory consumption.
|
|
160
|
+
|
|
161
|
+
### 3.3. Bundle Size Optimization
|
|
162
|
+
|
|
163
|
+
- **N/A:** Pytest itself doesn't directly impact bundle sizes, but your application code should be optimized separately.
|
|
164
|
+
|
|
165
|
+
### 3.4. Lazy Loading
|
|
166
|
+
|
|
167
|
+
- **N/A:** Lazy loading is more relevant to application code than pytest itself, but can be used within fixtures if necessary to defer initialization.
|
|
168
|
+
|
|
169
|
+
## 4. Security Best Practices
|
|
170
|
+
|
|
171
|
+
### 4.1. Common Vulnerabilities
|
|
172
|
+
|
|
173
|
+
- **Injection attacks:** Prevent injection attacks by validating and sanitizing user inputs.
|
|
174
|
+
- **Cross-site scripting (XSS):** Protect against XSS vulnerabilities by escaping user-generated content.
|
|
175
|
+
- **Authentication and authorization flaws:** Implement secure authentication and authorization mechanisms to protect sensitive data.
|
|
176
|
+
|
|
177
|
+
### 4.2. Input Validation
|
|
178
|
+
|
|
179
|
+
- **Validate all inputs:** Validate all user inputs to ensure they conform to expected formats and ranges.
|
|
180
|
+
- **Use parameterized tests:** Use parameterized tests to test input validation logic with a variety of inputs, including edge cases and invalid values.
|
|
181
|
+
|
|
182
|
+
### 4.3. Authentication and Authorization
|
|
183
|
+
|
|
184
|
+
- **Test authentication:** Write tests to verify that your authentication mechanisms are working correctly.
|
|
185
|
+
- **Test authorization:** Write tests to verify that users only have access to the resources they are authorized to access.
|
|
186
|
+
|
|
187
|
+
### 4.4. Data Protection
|
|
188
|
+
|
|
189
|
+
- **Encrypt sensitive data:** Encrypt sensitive data at rest and in transit.
|
|
190
|
+
- **Use secure storage:** Store sensitive data in secure storage locations with appropriate access controls.
|
|
191
|
+
|
|
192
|
+
### 4.5. Secure API Communication
|
|
193
|
+
|
|
194
|
+
- **Use HTTPS:** Always use HTTPS for API communication to protect data in transit.
|
|
195
|
+
- **Validate API responses:** Validate API responses to ensure they are valid and haven't been tampered with.
|
|
196
|
+
|
|
197
|
+
## 5. Testing Approaches
|
|
198
|
+
|
|
199
|
+
### 5.1. Unit Testing
|
|
200
|
+
|
|
201
|
+
- **Test individual units:** Unit tests should focus on testing individual functions, methods, or classes in isolation.
|
|
202
|
+
- **Mock dependencies:** Use mocking to isolate units under test from their dependencies.
|
|
203
|
+
|
|
204
|
+
### 5.2. Integration Testing
|
|
205
|
+
|
|
206
|
+
- **Test interactions:** Integration tests should focus on testing the interactions between different components of your application.
|
|
207
|
+
- **Use real dependencies (where appropriate):** For integration tests, it's often appropriate to use real dependencies, such as databases or external APIs, to ensure that the different components work together correctly. Consider using test containers for database and service dependencies.
|
|
208
|
+
|
|
209
|
+
### 5.3. End-to-End Testing
|
|
210
|
+
|
|
211
|
+
- **Test complete workflows:** End-to-end tests should focus on testing complete user workflows, from start to finish.
|
|
212
|
+
- **Use browser automation:** Use browser automation tools like Selenium or Playwright to simulate user interactions with your application.
|
|
213
|
+
|
|
214
|
+
### 5.4. Test Organization
|
|
215
|
+
|
|
216
|
+
- **Organize tests by feature:** Group tests by the feature they are testing to improve organization and maintainability.
|
|
217
|
+
- **Use clear naming conventions:** Use clear naming conventions for your tests and test files to indicate what they are testing.
|
|
218
|
+
|
|
219
|
+
### 5.5. Mocking and Stubbing
|
|
220
|
+
|
|
221
|
+
- **Use `mocker` fixture:** Use the `mocker` fixture provided by the `pytest-mock` plugin for mocking and stubbing.
|
|
222
|
+
- **Mock external dependencies:** Mock external dependencies, such as databases or APIs, to isolate your tests and prevent them from relying on external resources.
|
|
223
|
+
- **Use `autospec=True`:** Use `autospec=True` when mocking to ensure that your mocks have the same API as the original objects. This helps prevent errors caused by incorrect mock implementations.
|
|
224
|
+
|
|
225
|
+
python
|
|
226
|
+
def test_example(mocker):
|
|
227
|
+
mock_external_api = mocker.patch("module.external_api", autospec=True)
|
|
228
|
+
mock_external_api.return_value = {"data": "test data"}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
## 6. Common Pitfalls and Gotchas
|
|
232
|
+
|
|
233
|
+
### 6.1. Frequent Mistakes
|
|
234
|
+
|
|
235
|
+
- **Not isolating tests:** Failing to isolate tests can lead to unpredictable results and make it difficult to debug failures.
|
|
236
|
+
- **Testing implementation details:** Testing implementation details makes your tests brittle and difficult to maintain.
|
|
237
|
+
- **Ignoring warnings:** Ignoring warnings from pytest can mask underlying problems in your tests.
|
|
238
|
+
|
|
239
|
+
### 6.2. Edge Cases
|
|
240
|
+
|
|
241
|
+
- **Empty inputs:** Test your code with empty inputs to ensure it handles them gracefully.
|
|
242
|
+
- **Invalid inputs:** Test your code with invalid inputs to ensure it handles them correctly and raises appropriate exceptions.
|
|
243
|
+
- **Boundary conditions:** Test your code with boundary conditions to ensure it handles them correctly.
|
|
244
|
+
|
|
245
|
+
### 6.3. Version-Specific Issues
|
|
246
|
+
|
|
247
|
+
- **Check release notes:** Check the release notes for each new version of pytest to be aware of any breaking changes or new features.
|
|
248
|
+
- **Pin dependencies:** Pin your pytest dependency to a specific version to avoid unexpected behavior caused by updates.
|
|
249
|
+
|
|
250
|
+
### 6.4. Compatibility Concerns
|
|
251
|
+
|
|
252
|
+
- **Check compatibility:** Check the compatibility of pytest with other technologies you are using, such as specific versions of Python or Django.
|
|
253
|
+
|
|
254
|
+
### 6.5. Debugging Strategies
|
|
255
|
+
|
|
256
|
+
- **Use `--pdb`:** Use the `--pdb` option to drop into the Python debugger when a test fails.
|
|
257
|
+
- **Use logging:** Use logging to add debugging information to your tests.
|
|
258
|
+
- **Simplify tests:** Simplify failing tests to isolate the cause of the failure.
|
|
259
|
+
|
|
260
|
+
## 7. Tooling and Environment
|
|
261
|
+
|
|
262
|
+
### 7.1. Recommended Development Tools
|
|
263
|
+
|
|
264
|
+
- **IDE:** Use a good IDE with pytest support, such as VS Code with the Python extension, PyCharm, or Sublime Text with the appropriate plugins.
|
|
265
|
+
- **pytest-watch:** Use `pytest-watch` for automatic test rerunning on file changes. `pip install pytest-watch`, then run `ptw`.
|
|
266
|
+
|
|
267
|
+
### 7.2. Build Configuration
|
|
268
|
+
|
|
269
|
+
- **Use `pyproject.toml`:** Use a `pyproject.toml` file to configure your pytest settings.
|
|
270
|
+
|
|
271
|
+
toml
|
|
272
|
+
[tool.pytest.ini_options]
|
|
273
|
+
addopts = [
|
|
274
|
+
"--cov=my_app",
|
|
275
|
+
"--cov-report term-missing",
|
|
276
|
+
"-v",
|
|
277
|
+
]
|
|
278
|
+
testpaths = [
|
|
279
|
+
"tests",
|
|
280
|
+
]
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
### 7.3. Linting and Formatting
|
|
284
|
+
|
|
285
|
+
- **Use `flake8-pytest-style`:** Use the `flake8-pytest-style` plugin to enforce pytest-specific coding standards. `pip install flake8 flake8-pytest-style`
|
|
286
|
+
- **Use `black` or `autopep8`:** Use a code formatter like `black` or `autopep8` to ensure consistent code formatting. `pip install black`, then run `black .`
|
|
287
|
+
|
|
288
|
+
### 7.4. Deployment
|
|
289
|
+
|
|
290
|
+
- **Include tests in your deployment pipeline:** Ensure your tests are run as part of your deployment pipeline to prevent regressions.
|
|
291
|
+
- **Use a dedicated test environment:** Use a dedicated test environment to avoid interfering with your production environment.
|
|
292
|
+
|
|
293
|
+
### 7.5. CI/CD Integration
|
|
294
|
+
|
|
295
|
+
- **Integrate with CI/CD:** Integrate pytest with your CI/CD system, such as GitHub Actions, GitLab CI, or Jenkins, to automatically run your tests on every commit.
|
|
296
|
+
|
|
297
|
+
Example GitHub Actions workflow (`.github/workflows/test.yml`):
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
name: Test
|
|
301
|
+
on:
|
|
302
|
+
push:
|
|
303
|
+
branches: [ main ]
|
|
304
|
+
pull_request:
|
|
305
|
+
branches: [ main ]
|
|
306
|
+
jobs:
|
|
307
|
+
build:
|
|
308
|
+
runs-on: ubuntu-latest
|
|
309
|
+
steps:
|
|
310
|
+
- uses: actions/checkout@v3
|
|
311
|
+
- name: Set up Python 3.10
|
|
312
|
+
uses: actions/setup-python@v3
|
|
313
|
+
with:
|
|
314
|
+
python-version: "3.10"
|
|
315
|
+
- name: Install dependencies
|
|
316
|
+
run: |
|
|
317
|
+
python -m pip install --upgrade pip
|
|
318
|
+
pip install pytest pytest-cov flake8 flake8-pytest-style black
|
|
319
|
+
pip install -e . # Install your project in editable mode
|
|
320
|
+
- name: Lint with flake8
|
|
321
|
+
run: |
|
|
322
|
+
flake8 .
|
|
323
|
+
- name: Test with pytest
|
|
324
|
+
run: |
|
|
325
|
+
pytest --cov --cov-report xml
|
|
326
|
+
- name: Upload coverage to Codecov
|
|
327
|
+
uses: codecov/codecov-action@v3
|
|
328
|
+
with:
|
|
329
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
330
|
+
flags: unittests
|
|
331
|
+
env_vars: OS,PYTHON
|
|
332
|
+
name: codecov-pytest
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
By following these best practices, you can write effective and maintainable tests with pytest, improving the quality and reliability of your Python applications.---
|
|
336
|
+
alwaysApply: true
|
|
337
|
+
---
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Install Python packages, access Python CLI, run python code and scripts, execute Python commands, and manage Python environments. Use uv run for all Python execution instead of direct python commands.
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python Package Management with uv
|
|
7
|
+
|
|
8
|
+
Use uv exclusively for Python package management in all projects.
|
|
9
|
+
|
|
10
|
+
## Package Management Commands
|
|
11
|
+
|
|
12
|
+
- All Python dependencies **must be installed, synchronized, and locked** using uv
|
|
13
|
+
- Never use pip, pip-tools, poetry, or conda directly for dependency management
|
|
14
|
+
|
|
15
|
+
Use these commands
|
|
16
|
+
|
|
17
|
+
- Install dependencies: `uv add <package>`
|
|
18
|
+
- Remove dependencies: `uv remove <package>`
|
|
19
|
+
- Sync dependencies: `uv sync`
|
|
20
|
+
|
|
21
|
+
## Running Python Code
|
|
22
|
+
|
|
23
|
+
- Run a Python script with `uv run <script-name>.py`
|
|
24
|
+
- Run Python tools like Pytest with `uv run pytest` or `uv run ruff`
|
|
25
|
+
- Launch a Python repl with `uv run python`---
|
|
26
|
+
alwaysApply: true
|
|
27
|
+
------
|
|
28
|
+
alwaysApply: true
|
|
29
|
+
---
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: query-electiondatamy-api
|
|
3
|
+
description: Queries and analyzes Malaysian election data through the ElectionData.MY API. Use when the user asks for candidates, seats, parties, coalitions, elections, by-elections, contest results, turnout, majorities, or vote shares from ElectionData.MY.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Query ElectionData.MY API
|
|
7
|
+
|
|
8
|
+
Use the v1 API to answer focused questions about Malaysian election data. Read [reference.md](reference.md) before choosing endpoints or parameters.
|
|
9
|
+
|
|
10
|
+
## Scope
|
|
11
|
+
|
|
12
|
+
Use the API for focused, dynamic queries. Recommend the ElectionData.MY data lake instead when the user needs:
|
|
13
|
+
|
|
14
|
+
- bulk analysis or the full dataset;
|
|
15
|
+
- geospatial or boundary files;
|
|
16
|
+
- local analytical workflows better served by static files.
|
|
17
|
+
|
|
18
|
+
## Authentication and safety
|
|
19
|
+
|
|
20
|
+
- Base URL: `https://api.electiondata.my/v1`
|
|
21
|
+
- Only `GET` requests are supported.
|
|
22
|
+
- Every request requires `Authorization: Bearer <API key>`.
|
|
23
|
+
- Use the `ELECTIONDATAMY_API_KEY` environment variable. Check that it exists without printing it:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
test -n "$ELECTIONDATAMY_API_KEY"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- If it is missing, ask the user to set it in their terminal. Do not ask them to paste the key into chat.
|
|
30
|
+
- Never print, persist, commit, or interpolate the key as a literal.
|
|
31
|
+
|
|
32
|
+
## Query workflow
|
|
33
|
+
|
|
34
|
+
1. Translate the question into the entity, scope, election type, and date needed.
|
|
35
|
+
2. Select the endpoint chain from the reference.
|
|
36
|
+
3. When an identifier is unknown, call the relevant dropdown endpoint first. Do not guess UIDs, slugs, election identifiers, seat names, states, or dates.
|
|
37
|
+
4. Pass identifiers returned by discovery endpoints exactly as documented:
|
|
38
|
+
- Candidate queries use `uid`.
|
|
39
|
+
- Party queries use the selected entry's `maps_to` as `uid`, never its historical `uid`.
|
|
40
|
+
- Result detail queries reuse `seat`, `state`, and `date` from a companion endpoint without transformation.
|
|
41
|
+
5. URL-encode every query parameter. Prefer `curl --get --data-urlencode`.
|
|
42
|
+
6. Inspect the complete response before filtering or aggregating it.
|
|
43
|
+
7. Answer concisely, naming the endpoint and query scope used. Clearly separate returned fields from calculations or interpretation.
|
|
44
|
+
|
|
45
|
+
## Request pattern
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
curl --silent --show-error --fail-with-body --get \
|
|
49
|
+
"https://api.electiondata.my/v1/<endpoint>" \
|
|
50
|
+
--header "Authorization: Bearer ${ELECTIONDATAMY_API_KEY}" \
|
|
51
|
+
--data-urlencode "parameter=value"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For an endpoint without parameters, omit `--get` and `--data-urlencode`.
|
|
55
|
+
|
|
56
|
+
## Endpoint chaining
|
|
57
|
+
|
|
58
|
+
- Candidate lookup: `/candidates/dropdown` → `/candidates?uid=...` → optionally `/results`
|
|
59
|
+
- Seat history: `/seats/dropdown` → `/seats/results?slug=...` → optionally `/results`
|
|
60
|
+
- Party or coalition history: `/parties/dropdown` → `/parties/results`
|
|
61
|
+
- Election overview: `/elections/dropdown` → one or more of `/elections/by_party`, `/elections/by_seat`, `/elections/stats`
|
|
62
|
+
- By-election list: `/byelections` → optionally `/results`
|
|
63
|
+
- Contest detail: obtain `seat`, `state`, and `date` from a companion endpoint → `/results`
|
|
64
|
+
|
|
65
|
+
## Interpretation rules
|
|
66
|
+
|
|
67
|
+
- Treat ISO dates as polling dates unless the field explicitly describes a boundary-change event.
|
|
68
|
+
- Percentage fields are already percentages; do not multiply them by 100.
|
|
69
|
+
- `won_uncontested` can legitimately produce zero or null vote statistics.
|
|
70
|
+
- In party queries, `{"results":[]}` means no contests in that scope, not an API error.
|
|
71
|
+
- With seat `lineage=true`, distinguish election rows by the presence of `election_name`; rows without it are boundary-change events.
|
|
72
|
+
- Preserve the API's `parlimen` and `dun` values when filtering.
|
|
73
|
+
- Do not infer current party identity from `known_as`; explain historical names and canonical mappings when relevant.
|
|
74
|
+
|
|
75
|
+
## Errors
|
|
76
|
+
|
|
77
|
+
- `400`: missing or invalid parameters, invalid combinations, malformed date, or missing version prefix.
|
|
78
|
+
- `401`: missing or invalid API key.
|
|
79
|
+
- `404`: unknown endpoint or no matching resource.
|
|
80
|
+
- `405`: method other than `GET`.
|
|
81
|
+
- `500`: server-side failure.
|
|
82
|
+
|
|
83
|
+
Error bodies contain an `error` field. Report the useful message, correct the request when possible, and do not silently reinterpret an invalid query.
|
|
84
|
+
|
|
85
|
+
## Answer quality
|
|
86
|
+
|
|
87
|
+
- State material filters such as state, election, election type, lineage mode, and date.
|
|
88
|
+
- Include units for vote counts, seats, turnout, and percentages.
|
|
89
|
+
- For comparisons, verify that scopes and denominators match.
|
|
90
|
+
- Mention missing or null data rather than silently dropping it.
|
|
91
|
+
- Do not claim the API is real-time; dropdown datasets update around election-related publication events described in the docs.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# ElectionData.MY v1 reference
|
|
2
|
+
|
|
3
|
+
Base URL: `https://api.electiondata.my/v1`
|
|
4
|
+
|
|
5
|
+
All endpoints are authenticated `GET` requests.
|
|
6
|
+
|
|
7
|
+
## Candidates
|
|
8
|
+
|
|
9
|
+
### `/candidates/dropdown`
|
|
10
|
+
|
|
11
|
+
No parameters. Returns an array of candidates ordered by contests, with:
|
|
12
|
+
|
|
13
|
+
- `uid`, `name`
|
|
14
|
+
- `c` contests, `w` wins, `l` losses
|
|
15
|
+
|
|
16
|
+
Use `uid` in `/candidates`.
|
|
17
|
+
|
|
18
|
+
### `/candidates`
|
|
19
|
+
|
|
20
|
+
Required: `uid`
|
|
21
|
+
|
|
22
|
+
Returns an array ordered newest to oldest. Each row identifies the candidate, election, seat, state, party and coalition, votes, vote percentage, polling date, and result.
|
|
23
|
+
|
|
24
|
+
Result values: `won`, `won_uncontested`, `lost`, `lost_deposit`.
|
|
25
|
+
|
|
26
|
+
## Seats
|
|
27
|
+
|
|
28
|
+
### `/seats/dropdown`
|
|
29
|
+
|
|
30
|
+
No parameters. Response: `{ "seats": [...] }`
|
|
31
|
+
|
|
32
|
+
Each row contains:
|
|
33
|
+
|
|
34
|
+
- `seat`: full current seat name
|
|
35
|
+
- `slug`: identifier for `/seats/results`
|
|
36
|
+
- `type`: `parlimen` or `dun`
|
|
37
|
+
|
|
38
|
+
### `/seats/results`
|
|
39
|
+
|
|
40
|
+
Required: `slug`
|
|
41
|
+
|
|
42
|
+
Optional: `lineage=true|false` (default `false`)
|
|
43
|
+
|
|
44
|
+
Response: `{ "results": [...] }`, newest first.
|
|
45
|
+
|
|
46
|
+
Election rows include `election_name`, `seat`, `state`, `date`, winner `name`, party and coalition identifiers, majority, and turnout.
|
|
47
|
+
|
|
48
|
+
With `lineage=true`, the array also contains boundary-change rows with `date`, `change_en`, and `change_ms`. A row with `election_name` is an election result; a row without it is a boundary-change event.
|
|
49
|
+
|
|
50
|
+
## Parties and coalitions
|
|
51
|
+
|
|
52
|
+
### `/parties/dropdown`
|
|
53
|
+
|
|
54
|
+
No parameters. Response: `{ "data": [...] }`
|
|
55
|
+
|
|
56
|
+
Each row contains `type`, `uid`, `maps_to`, `acronym`, `name_en`, and `name_bm`.
|
|
57
|
+
|
|
58
|
+
Historical names may have legacy UIDs. Always pass `maps_to`, not `uid`, to `/parties/results`.
|
|
59
|
+
|
|
60
|
+
### `/parties/results`
|
|
61
|
+
|
|
62
|
+
Required:
|
|
63
|
+
|
|
64
|
+
- `type`: `party` or `coalition`
|
|
65
|
+
- `uid`: the dropdown row's `maps_to`
|
|
66
|
+
- `state`
|
|
67
|
+
- `election_type`: `parlimen` or `dun`
|
|
68
|
+
|
|
69
|
+
Valid states:
|
|
70
|
+
|
|
71
|
+
`Malaysia`, `Semenanjung`, `Johor`, `Kedah`, `Kelantan`, `Melaka`, `Negeri Sembilan`, `Pahang`, `Perak`, `Perlis`, `Pulau Pinang`, `Sabah`, `Sarawak`, `Selangor`, `Terengganu`, `W.P. Kuala Lumpur`, `W.P. Labuan`, `W.P. Putrajaya`
|
|
72
|
+
|
|
73
|
+
`dun` is invalid for `Malaysia`, `Semenanjung`, and every `W.P. *` state.
|
|
74
|
+
|
|
75
|
+
Response: `{ "results": [...] }`, newest first. Rows contain historical identity (`known_as_uid`, `known_as`), election and date, seats contested and won, seat percentages, votes, and vote percentage. An empty `results` array is valid.
|
|
76
|
+
|
|
77
|
+
## Elections
|
|
78
|
+
|
|
79
|
+
### `/elections/dropdown`
|
|
80
|
+
|
|
81
|
+
No parameters. Response: `{ "elections": [...] }`
|
|
82
|
+
|
|
83
|
+
Each row contains `state`, `type`, `election`, and `date`. Pass `state` and `election` directly to the following election endpoints.
|
|
84
|
+
|
|
85
|
+
### `/elections/by_party`
|
|
86
|
+
|
|
87
|
+
Required: `state`, `election`
|
|
88
|
+
|
|
89
|
+
Response: `{ "by_party": [...] }`, ordered by votes descending. Rows include party and coalition identifiers, seats contested and won, total seats, seat percentages, votes, total valid votes, and vote percentage.
|
|
90
|
+
|
|
91
|
+
### `/elections/by_seat`
|
|
92
|
+
|
|
93
|
+
Required: `state`, `election`
|
|
94
|
+
|
|
95
|
+
Response: `{ "by_seat": [...] }`, one row per constituency. Rows contain the seat, date, winner, winning and losing parties and coalitions, candidate count, registered voters, turnout, majority, and rejected-vote statistics.
|
|
96
|
+
|
|
97
|
+
### `/elections/stats`
|
|
98
|
+
|
|
99
|
+
Required: `state`, `election`
|
|
100
|
+
|
|
101
|
+
Response: `{ "stats": [<single aggregate row>] }`
|
|
102
|
+
|
|
103
|
+
The row contains registered voters, turnout, turnout percentage, rejected votes, rejected-vote percentage, and candidate count.
|
|
104
|
+
|
|
105
|
+
## By-elections
|
|
106
|
+
|
|
107
|
+
### `/byelections`
|
|
108
|
+
|
|
109
|
+
No parameters. Response: `{ "data": [...] }`, newest first.
|
|
110
|
+
|
|
111
|
+
Each row contains `seat`, `state`, `date`, winner, party and coalition identifiers, registered voters, turnout, rejected votes, majority, and corresponding percentages.
|
|
112
|
+
|
|
113
|
+
Use a row's `seat`, `state`, and `date` to request full contest details from `/results`.
|
|
114
|
+
|
|
115
|
+
## Contest details
|
|
116
|
+
|
|
117
|
+
### `/results`
|
|
118
|
+
|
|
119
|
+
Required:
|
|
120
|
+
|
|
121
|
+
- `seat`
|
|
122
|
+
- `state`
|
|
123
|
+
- `date` in `YYYY-MM-DD`
|
|
124
|
+
|
|
125
|
+
Obtain all three values from candidates, seats, elections, or by-elections responses and pass them through unchanged.
|
|
126
|
+
|
|
127
|
+
Response:
|
|
128
|
+
|
|
129
|
+
- `ballot`: candidates ordered by votes descending, with party, coalition, votes, vote percentage, and result
|
|
130
|
+
- `stats`: a one-row array containing date, registered voters, turnout, rejected votes, majority, and corresponding percentages
|
|
131
|
+
|
|
132
|
+
For uncontested wins, vote counts may be zero and percentage statistics may be null.
|
|
133
|
+
|
|
134
|
+
## Common examples
|
|
135
|
+
|
|
136
|
+
Find a candidate's history:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
curl --silent --show-error --fail-with-body --get \
|
|
140
|
+
"https://api.electiondata.my/v1/candidates" \
|
|
141
|
+
--header "Authorization: Bearer ${ELECTIONDATAMY_API_KEY}" \
|
|
142
|
+
--data-urlencode "uid=CMVBA"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Get election results by party:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
curl --silent --show-error --fail-with-body --get \
|
|
149
|
+
"https://api.electiondata.my/v1/elections/by_party" \
|
|
150
|
+
--header "Authorization: Bearer ${ELECTIONDATAMY_API_KEY}" \
|
|
151
|
+
--data-urlencode "state=Malaysia" \
|
|
152
|
+
--data-urlencode "election=GE-15"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Get one contest:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
curl --silent --show-error --fail-with-body --get \
|
|
159
|
+
"https://api.electiondata.my/v1/results" \
|
|
160
|
+
--header "Authorization: Bearer ${ELECTIONDATAMY_API_KEY}" \
|
|
161
|
+
--data-urlencode "seat=P.001 Padang Besar" \
|
|
162
|
+
--data-urlencode "state=Perlis" \
|
|
163
|
+
--data-urlencode "date=2022-11-19"
|
|
164
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ELECTIONDATAMY_API_KEY=""
|