77- Python 3.13+
88- PDM
99- Git
10+ - ASDF (recommended)
1011
1112### Download and Install the Source Code
1213
@@ -49,8 +50,9 @@ fitbit-client/
4950│ ├── resources/
5051│ │ ├── __init__.py
5152│ │ ├── [resource modules]
52- │ │ ├── base.py
53- │ │ └── constants.py
53+ │ │ ├── _base.py
54+ │ │ ├── _pagination.py
55+ │ │ └── _constants.py
5456│ ├── utils/
5557│ │ ├── __init__.py
5658│ │ ├── curl_debug_mixin.py
@@ -59,26 +61,22 @@ fitbit-client/
5961│ │ ├── pagination_validation.py
6062│ │ └── types.py
6163│ └── exceptions.py
62- ├── tests/
63- │ ├── auth/
64- │ ├── resources/
65- │ └── utils/
66- └── [project files]
64+ └── tests/
65+ │ ├── fitbit_client/
66+ │ ├── auth/
67+ │ ├── resources/
68+ │ └── utils/
69+ └── [project files]
6770```
6871
69- ## Goals, Notes, and TODOs
70-
71- For now these are just in [ TODO.md] ( TODO.md ) ; bigger work will eventually move
72- to Github tickets.
73-
7472## Development Tools and Standards
7573
7674### Code Formatting and Style
7775
7876- Black for code formatting (100 character line length)
7977- isort for import sorting
80- - Type hints required for all code
81- - Docstrings required for all public methods
78+ - Type hints required for all code (enforced by ` mypy ` )
79+ - Docstrings required for all public methods (enforced by ` test_docscrings.py ` )
8280
8381### Import Style
8482
@@ -102,7 +100,7 @@ import typing
102100import datetime
103101```
104102
105- The one exception to this rule is when an entire module needs to be ` mock ` ed for
103+ The one exception to this rule is when an entire module needs to be mocked for
106104testing, in which case, at least for the ` json ` package from the standard
107105library, the entire package has to be imported. So ` import json ` is ok when that
108106circumstance arises.
@@ -120,46 +118,29 @@ Follow your nose from `client.py` and the structure should be very clear.
120118#### Method Structure
121119
122120- Include comprehensive docstrings with Args sections
123- - Keep parameter naming consistent across methods
124- - Use "-" as default for user_id parameters
125- - Return Dict [ str, Any ] for most methods that return data
126- - Return None for delete operations
121+ - Keep parameter naming consistent across methods (see [ Naming ] ( docs/NAMING.md ) )
122+ - Return ` JSONDict ` for ` JSONList ` for most methods ( ` get_activity_tcx ` returns
123+ XML as a string)
124+ - Return ` None ` for delete operations
127125
128126### Error Handling
129127
130- The codebase implements a comprehensive error handling system through
131- [ ` exceptions.py ` ] ( fitbit_client/exceptions.py ) :
132-
133- 1 . A base FitbitAPIException that captures:
134-
135- - HTTP status code
136- - Error type
137- - Error message
138- - Field name (when applicable)
139-
140- 2 . Specialized exceptions for different error scenarios:
141-
142- - InvalidRequestException for malformed requests
143- - ValidationException for parameter validation failures
144- - AuthorizationException for authentication issues
145- - RateLimitExceededException for API throttling
146- - SystemException for server-side errors
147-
148- 3 . Mapping from HTTP status codes and API error types to appropriate exception
149- classes
128+ The codebase implements a comprehensive error handling system. See
129+ [ ERROR_HANDLING] ( docs/ERROR_HANDLING.md ) and
130+ [ ` exceptions.py ` ] ( fitbit_client/exceptions.py ) .
150131
151132### Enum Usage
152133
153134- Only use enums for validating request parameters, not responses
154- - Place all enums in constants.py
135+ - Place all enums in [ ` constants.py ` ] ( fitbit_client/resources/_constants.py )
155136- Only import enums that are actively used in the class
156137
157138## Logging System
158139
159140The project implements two different logs in through the
160- [ ` BaseResource ` ] ( fitbit_client/resources/base .py ) class: application logging for
161- API interactions and data logging for tracking important response fields. See
162- [ LOGGING] ( docs/LOGGING.md ) for details.
141+ [ ` BaseResource ` ] ( fitbit_client/resources/_base .py ) class: application logging
142+ for API interactions and data logging for tracking important response fields.
143+ See [ LOGGING] ( docs/LOGGING.md ) for details.
163144
164145## API Design
165146
@@ -190,77 +171,27 @@ client.get_profile()
190171client.get_daily_activity_summary(date = " 2025-03-06" )
191172```
192173
193- Method aliases were implemented for several important reasons:
194-
195- 1 . ** Reduced Verbosity** : Typing ` client.resource_name.method_name(...) ` with
196- many parameters can be tedious, especially when used frequently.
197-
198- 2 . ** Flatter API Surface** : Many modern APIs prefer a flatter design that avoids
199- deep nesting, making the API more straightforward to use.
200-
201- 3 . ** Method Name Uniqueness** : All resource methods in the Fitbit API have
202- unique names (e.g., there's only one ` get_profile() ` method), making it safe
203- to expose these methods directly on the client.
204-
205- 4 . ** Preserve Both Options** : By maintaining both the resource-based access and
206- direct aliases, developers can choose the approach that best fits their needs
207- \- organization or conciseness.
208-
209- All method aliases are set up in the ` _set_up_method_aliases() ` method in the
174+ Method aliases were implemented because yyping
175+ ` client.resource_name.method_name(...) ` with many parameters can be tedious,
176+ especially when used frequently. All method aliases are set up in the
177+ ` _set_up_method_aliases() ` method in the
210178[ ` FitbitClient ` ] ( fitbit_client/client.py ) class, which is called during
211179initialization. Each alias is a direct reference to the corresponding resource
212180method, ensuring consistent behavior regardless of how the method is accessed.
213181
214182## Testing
215183
216184The project uses pytest for testing and follows a consistent testing approach
217- across all components.
185+ across all components. 100% coverage is expected.
218186
219187### Test Organization
220188
221- The test directory mirrors the main package structure (except that the root is
222- named "test" rather than "fitbit_client"), with corresponding test modules for
223- each component:
224-
225- - auth/: Tests for authentication and OAuth functionality
226- - client/: Tests for the main client implementation
227- - resources/: Tests for individual API resource implementations
228-
229- ### Standard Test Fixtures
230-
231- The test suite provides several standard fixtures for use across test modules:
189+ The test directory mirrors the main package structure within the ` test `
190+ directory. For the most part, the naming is 1:1 (` test_blah.py ` ) or otherwise
191+ obvious--many tests modules were getting quite long and broken out either into
192+ directories or with names that make it obvious as to hwat they are testing.
232193
233- ``` python
234- @fixture
235- def mock_oauth_session ():
236- """ Provides a mock OAuth session for testing resources"""
237- return Mock()
238-
239- @fixture
240- def mock_logger ():
241- """ Provides a mock logger for testing logging behavior"""
242- return Mock()
243-
244- @fixture
245- def base_resource (mock_oauth_session , mock_logger ):
246- """ Creates a resource instance with mocked dependencies"""
247- with patch(" fitbit_client.resources._base.getLogger" , return_value = mock_logger):
248- return BaseResource(mock_oauth_session, " en_US" , " en_US" )
249- ```
250-
251- ### Error Handling Tests
252-
253- Tests verify proper error handling across the codebase. Common patterns include:
254-
255- ``` python
256- def test_http_error_handling (resource ):
257- """ Tests that HTTP errors are properly converted to exceptions"""
258- with raises(InvalidRequestException) as exc_info:
259- # Test code that should raise the exception
260- pass
261- assert exc_info.value.status_code == 400
262- assert exc_info.value.error_type == " validation"
263- ```
194+ All resource mocks are in the root [ conftest.py] ( tests/conftest.py ) .
264195
265196### Response Mocking
266197
@@ -332,39 +263,4 @@ git commit --no-verify -m "Your commit message"
332263
333264## Release Process
334265
335- This section will be documented as we near our first release.
336-
337- ## Pagination Implementation
338-
339- The pagination implementation uses the following approach:
340-
341- ### Pagination Iterator
342-
343- - Uses the ` PaginatedIterator ` class that implements the Python ` Iterator `
344- protocol
345- - Automatically handles fetching the next page when needed using the ` next ` URL
346- from pagination metadata
347- - Properly handles edge cases like invalid responses, missing pagination data,
348- and API errors
349-
350- ### Type Safety
351-
352- - Uses ` TYPE_CHECKING ` from the typing module to avoid circular imports at
353- runtime
354- - Maintains complete type safety and mypy compatibility
355- - All pagination-related code has 100% test coverage
356-
357- ### Resource Integration
358-
359- Each endpoint that supports pagination has an ` as_iterator ` parameter that, when
360- set to ` True ` , returns a ` PaginatedIterator ` instead of the raw API response.
361- This makes it easy to iterate through all pages of results without manually
362- handling pagination.
363-
364- ## Intraday Data Support
365-
366- This client implements intraday data endpoints (detailed heart rate, steps, etc)
367- through the ` IntradayResource ` class. These endpoints have some special
368- requirements if you're using them for anyone other that yourself. See the
369- [ Intraday API documentation] ( https://dev.fitbit.com/build/reference/web-api/intraday/ )
370- for more details.
266+ _ This section will be documented as we near our first release._
0 commit comments