bakalari 1.0.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.
- bakalari-1.0.0/CHANGELOG.md +227 -0
- bakalari-1.0.0/CODE_OF_CONDUCT.md +36 -0
- bakalari-1.0.0/CONTRIBUTING.md +197 -0
- bakalari-1.0.0/LICENSE +21 -0
- bakalari-1.0.0/MANIFEST.in +19 -0
- bakalari-1.0.0/PKG-INFO +247 -0
- bakalari-1.0.0/README.md +199 -0
- bakalari-1.0.0/SECURITY.md +53 -0
- bakalari-1.0.0/bakalari/__init__.py +205 -0
- bakalari-1.0.0/bakalari/__main__.py +5 -0
- bakalari-1.0.0/bakalari/_core.py +579 -0
- bakalari-1.0.0/bakalari/_mcp_tools.py +237 -0
- bakalari-1.0.0/bakalari/_parse.py +289 -0
- bakalari-1.0.0/bakalari/_version.py +1 -0
- bakalari-1.0.0/bakalari/absence.py +181 -0
- bakalari-1.0.0/bakalari/cli.py +1993 -0
- bakalari-1.0.0/bakalari/client.py +86 -0
- bakalari-1.0.0/bakalari/errors.py +88 -0
- bakalari-1.0.0/bakalari/events.py +185 -0
- bakalari-1.0.0/bakalari/gdpr.py +160 -0
- bakalari-1.0.0/bakalari/homeworks.py +132 -0
- bakalari-1.0.0/bakalari/http.py +153 -0
- bakalari-1.0.0/bakalari/komens.py +974 -0
- bakalari-1.0.0/bakalari/marks.py +625 -0
- bakalari-1.0.0/bakalari/mcp.py +282 -0
- bakalari-1.0.0/bakalari/models.py +71 -0
- bakalari-1.0.0/bakalari/payments.py +307 -0
- bakalari-1.0.0/bakalari/public_timetable.py +1024 -0
- bakalari-1.0.0/bakalari/py.typed +0 -0
- bakalari-1.0.0/bakalari/schools.py +259 -0
- bakalari-1.0.0/bakalari/subjects.py +201 -0
- bakalari-1.0.0/bakalari/substitutions.py +76 -0
- bakalari-1.0.0/bakalari/teacher.py +580 -0
- bakalari-1.0.0/bakalari/timetable.py +660 -0
- bakalari-1.0.0/bakalari/user.py +374 -0
- bakalari-1.0.0/bakalari.egg-info/PKG-INFO +247 -0
- bakalari-1.0.0/bakalari.egg-info/SOURCES.txt +121 -0
- bakalari-1.0.0/bakalari.egg-info/dependency_links.txt +1 -0
- bakalari-1.0.0/bakalari.egg-info/entry_points.txt +3 -0
- bakalari-1.0.0/bakalari.egg-info/requires.txt +15 -0
- bakalari-1.0.0/bakalari.egg-info/top_level.txt +1 -0
- bakalari-1.0.0/docs/cli.md +1500 -0
- bakalari-1.0.0/docs/endpoints.md +267 -0
- bakalari-1.0.0/docs/guide.md +1158 -0
- bakalari-1.0.0/docs/library.md +917 -0
- bakalari-1.0.0/docs/mcp.md +786 -0
- bakalari-1.0.0/examples/README.md +38 -0
- bakalari-1.0.0/examples/class_fund_qr.py +75 -0
- bakalari-1.0.0/examples/find_school.py +67 -0
- bakalari-1.0.0/examples/homeworks_ics.py +119 -0
- bakalari-1.0.0/examples/new_marks.py +65 -0
- bakalari-1.0.0/examples/persistent_session.py +134 -0
- bakalari-1.0.0/examples/unread_messages.py +79 -0
- bakalari-1.0.0/examples/weekly_timetable.py +77 -0
- bakalari-1.0.0/pyproject.toml +133 -0
- bakalari-1.0.0/setup.cfg +4 -0
- bakalari-1.0.0/tests/__init__.py +0 -0
- bakalari-1.0.0/tests/test_live.py +651 -0
- bakalari-1.0.0/tests/test_mcp_sdk.py +54 -0
- bakalari-1.0.0/unittests/__init__.py +0 -0
- bakalari-1.0.0/unittests/fixtures/absence.json +29 -0
- bakalari-1.0.0/unittests/fixtures/actual.json +73 -0
- bakalari-1.0.0/unittests/fixtures/events.json +208 -0
- bakalari-1.0.0/unittests/fixtures/events_after_break.json +108 -0
- bakalari-1.0.0/unittests/fixtures/gdpr_child_consents.json +64 -0
- bakalari-1.0.0/unittests/fixtures/gdpr_commissioners.json +13 -0
- bakalari-1.0.0/unittests/fixtures/gdpr_consents.json +24 -0
- bakalari-1.0.0/unittests/fixtures/homeworks.json +38 -0
- bakalari-1.0.0/unittests/fixtures/komens_message_types.json +135 -0
- bakalari-1.0.0/unittests/fixtures/komens_message_types_reply.json +25 -0
- bakalari-1.0.0/unittests/fixtures/komens_received.json +101 -0
- bakalari-1.0.0/unittests/fixtures/komens_received_detail.json +29 -0
- bakalari-1.0.0/unittests/fixtures/komens_sent.json +69 -0
- bakalari-1.0.0/unittests/fixtures/komens_sent_detail.json +81 -0
- bakalari-1.0.0/unittests/fixtures/marks.json +234 -0
- bakalari-1.0.0/unittests/fixtures/marks_final.json +114 -0
- bakalari-1.0.0/unittests/fixtures/marks_measures.json +20 -0
- bakalari-1.0.0/unittests/fixtures/marks_what_if.json +91 -0
- bakalari-1.0.0/unittests/fixtures/payments_classfund.json +41 -0
- bakalari-1.0.0/unittests/fixtures/payments_info.json +8 -0
- bakalari-1.0.0/unittests/fixtures/payments_summary.json +5 -0
- bakalari-1.0.0/unittests/fixtures/permanent.json +60 -0
- bakalari-1.0.0/unittests/fixtures/public_timetable_actual.html +259 -0
- bakalari-1.0.0/unittests/fixtures/public_timetable_actual.json +653 -0
- bakalari-1.0.0/unittests/fixtures/public_timetable_errinfo.html +43 -0
- bakalari-1.0.0/unittests/fixtures/public_timetable_index.html +205 -0
- bakalari-1.0.0/unittests/fixtures/public_timetable_legacy.html +121 -0
- bakalari-1.0.0/unittests/fixtures/public_timetable_permanent.html +258 -0
- bakalari-1.0.0/unittests/fixtures/schools_kocourkov.json +35 -0
- bakalari-1.0.0/unittests/fixtures/schools_municipalities.json +46 -0
- bakalari-1.0.0/unittests/fixtures/subjects.json +20 -0
- bakalari-1.0.0/unittests/fixtures/subjects_themes.json +17 -0
- bakalari-1.0.0/unittests/fixtures/substitutions.json +54 -0
- bakalari-1.0.0/unittests/fixtures/teacher_classbook.json +82 -0
- bakalari-1.0.0/unittests/fixtures/teacher_classbook_saved.json +28 -0
- bakalari-1.0.0/unittests/fixtures/teacher_lesson.json +60 -0
- bakalari-1.0.0/unittests/fixtures/teacher_marking.json +86 -0
- bakalari-1.0.0/unittests/fixtures/timetable_class.json +118 -0
- bakalari-1.0.0/unittests/fixtures/timetable_holiday.json +35 -0
- bakalari-1.0.0/unittests/fixtures/user_parent.json +245 -0
- bakalari-1.0.0/unittests/fixtures/user_student.json +49 -0
- bakalari-1.0.0/unittests/fixtures/webmodule.json +22 -0
- bakalari-1.0.0/unittests/support.py +147 -0
- bakalari-1.0.0/unittests/test_absence.py +229 -0
- bakalari-1.0.0/unittests/test_cli.py +1849 -0
- bakalari-1.0.0/unittests/test_core.py +505 -0
- bakalari-1.0.0/unittests/test_events.py +293 -0
- bakalari-1.0.0/unittests/test_gdpr.py +183 -0
- bakalari-1.0.0/unittests/test_homeworks.py +280 -0
- bakalari-1.0.0/unittests/test_http.py +149 -0
- bakalari-1.0.0/unittests/test_komens.py +1027 -0
- bakalari-1.0.0/unittests/test_marks.py +696 -0
- bakalari-1.0.0/unittests/test_mcp.py +386 -0
- bakalari-1.0.0/unittests/test_package.py +400 -0
- bakalari-1.0.0/unittests/test_parse.py +184 -0
- bakalari-1.0.0/unittests/test_payments.py +354 -0
- bakalari-1.0.0/unittests/test_public_timetable.py +507 -0
- bakalari-1.0.0/unittests/test_schools.py +380 -0
- bakalari-1.0.0/unittests/test_subjects.py +302 -0
- bakalari-1.0.0/unittests/test_substitutions.py +219 -0
- bakalari-1.0.0/unittests/test_teacher.py +457 -0
- bakalari-1.0.0/unittests/test_timetable.py +618 -0
- bakalari-1.0.0/unittests/test_user.py +376 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2026-10-26
|
|
9
|
+
|
|
10
|
+
Initial release of the `bakalari` distribution: an unofficial Python client, CLI and MCP server for
|
|
11
|
+
Bakaláři REST API v3, using only the standard library and supporting Python 3.9 through 3.14.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- MCP server mode through `bakalari mcp`, `bakalari-mcp` and `python -m bakalari.mcp`, with no new runtime
|
|
16
|
+
dependencies and Python 3.9 support. The stdio server provides 50 read tools across the API modules and school
|
|
17
|
+
directory, plus 11 opt-in write tools with `--allow-writes`.
|
|
18
|
+
- Explicit tool schemas and validation, protocol/version negotiation, structured results, redacted errors,
|
|
19
|
+
bounded stdio frames, base64 attachment transfers, and automatic CLI session renewal/persistence.
|
|
20
|
+
- MCP setup and tool reference in `docs/mcp.md`, including macOS/Linux and Windows setup, multiple accounts,
|
|
21
|
+
read/write workflows, attachment examples, troubleshooting, an offline connection check and Python integration.
|
|
22
|
+
README, CLI and library guides link to the full usage guide.
|
|
23
|
+
- Offline MCP protocol/integration tests and a CI interoperability check using the official MCP Python client.
|
|
24
|
+
|
|
25
|
+
#### Client
|
|
26
|
+
|
|
27
|
+
- `BakalariClient` logs in like the official mobile app (the OAuth2 password grant on `POST /api/login`, client id
|
|
28
|
+
`ANDR`) on the first request that needs a token, renews the access token with the refresh token `renew_margin`
|
|
29
|
+
seconds (60) before it expires, renews and retries once after HTTP 401, and logs in again with the password when
|
|
30
|
+
the refresh token is rejected. `login()`, `refresh()`, `ensure_token()`, `logout()`, `token_info` (`TokenInfo`:
|
|
31
|
+
user id, scope, API version, expiry; never a token) and `is_authenticated` give explicit control, and a `with`
|
|
32
|
+
block logs out at its end.
|
|
33
|
+
- `BakalariClient.from_env()` reads the address from `BAKALARI_URL` and the login from `BAKALARI_USERNAME` and
|
|
34
|
+
`BAKALARI_PASSWORD`, or from other variables with `username_env=` and `password_env=` (e.g. another child's login).
|
|
35
|
+
- Sessions: `export_session()` and `import_session()` with `SessionState` (`to_dict()` and `from_dict()`), a client
|
|
36
|
+
started from a `refresh_token` alone, and the `on_session_update` callback after every login and renewal, so that
|
|
37
|
+
an application can keep the newest refresh token and continue without the password.
|
|
38
|
+
- Retries: `retries` (0 by default) repeats `GET`, `PUT` and `DELETE` requests after a connection error or HTTP 502,
|
|
39
|
+
503 or 504, waiting `retry_backoff` (0.5 s) and twice as long before each further retry; `POST` requests, logins
|
|
40
|
+
included, are never repeated. `timeout` (30 s) applies to every request.
|
|
41
|
+
- Transports: the default `urllib_transport()` (no cookie jar, the usual proxy variables, an optional `ssl_context`)
|
|
42
|
+
or any function with the `Transport` signature that returns an `HttpResponse`, e.g. canned answers in tests.
|
|
43
|
+
- `normalize_base_url()` accepts an address copied from the browser: it adds `https://` and drops a page path, a
|
|
44
|
+
query and a trailing slash. Every request sends `Accept-Language: cs`, without which some installations answer
|
|
45
|
+
HTTP 500.
|
|
46
|
+
- Raw access with the same token handling: `request()` (any method, query parameters, a form, JSON or raw body,
|
|
47
|
+
extra headers, an expected status or a set of them), `get_json()`, `post_json()`, `api_version()` and
|
|
48
|
+
`available_api_versions()`.
|
|
49
|
+
- One client can be shared between threads: a lock guards the tokens.
|
|
50
|
+
- Logging through the `bakalari.client` and `bakalari.schools` loggers: the method, path and status of every
|
|
51
|
+
request, the logins, renewals and retries.
|
|
52
|
+
|
|
53
|
+
#### Typed models and contract checks
|
|
54
|
+
|
|
55
|
+
- The responses become dataclasses based on `Model` (frozen, except `Timetable`), with `to_dict()` (JSON-ready data)
|
|
56
|
+
and `raw` (the server's object, for the keys a model does not map). Ids are kept exactly as sent, space padding included, and
|
|
57
|
+
percent-encoded in paths; display strings are trimmed; timestamps are timezone-aware `datetime` objects and
|
|
58
|
+
calendar days `date` objects; decimal commas are understood; missing and `null` fields get defaults.
|
|
59
|
+
- `Item` for the `{Id, Abbrev, Name}` objects, `AttachmentInfo`, `format()` methods for readable one-line output, and
|
|
60
|
+
`from_json()` class methods and `parse_...()` functions for offline parsing.
|
|
61
|
+
- Contract checks: the methods with a `validate` parameter (true by default) check the essential shape of each
|
|
62
|
+
payload and raise `BakalariContractError` listing every problem (`TimetableContractError` for timetables). A body
|
|
63
|
+
that is not JSON, such as an HTML error page with HTTP 200, is a contract error too, and counters must be numbers.
|
|
64
|
+
`validate=False` parses leniently.
|
|
65
|
+
- Errors: `BakalariError` and its subclasses `BakalariConfigError`, `BakalariConnectionError`, `BakalariContractError`
|
|
66
|
+
and `BakalariHTTPError`, which has `method`, `path`, `status`, `expected`, `body` and the server's `message`, and
|
|
67
|
+
the subclasses `BakalariAuthError`, `BakalariPermissionError` (HTTP 403: a module or right is missing, or the
|
|
68
|
+
endpoint is for teachers) and `BakalariNotFoundError` (HTTP 404). Wrong arguments raise `ValueError` or `TypeError`
|
|
69
|
+
before any request.
|
|
70
|
+
|
|
71
|
+
#### Endpoints
|
|
72
|
+
|
|
73
|
+
Typed methods for 71 endpoints, one module per area. docs/endpoints.md lists every known endpoint with its
|
|
74
|
+
verification status: 41 of the 71 were verified against a live installation and 23 more by route and method.
|
|
75
|
+
|
|
76
|
+
- Account (`bakalari.user`): `user_profile()`, which returns a `UserProfile` (user type, names, class, school, school
|
|
77
|
+
year and semester, the students of a parent account, the modules and rights with `has_module()` and
|
|
78
|
+
`has_right()`), `enabled_modules()`, `student_at_school()`, `web_modules()`, `login_token()`, `web_login_url()` (a
|
|
79
|
+
one-time link that opens the Bakaláři web logged in) and `register_notification()`.
|
|
80
|
+
- Timetable (`bakalari.timetable`): `actual_timetable()`, `permanent_timetable()`, `class_timetable()` (a whole
|
|
81
|
+
class with all its groups) and `timetable_json()`, with the lessons' homeworks, notices, week themes and times.
|
|
82
|
+
- Substitutions (`bakalari.substitutions`): `substitutions()`, the changes of 14 days grouped by day.
|
|
83
|
+
- Marks (`bakalari.marks`): `marks()` (optionally only `since` a date), `new_marks_count()`, `final_marks()`,
|
|
84
|
+
`pedagogical_measures()`, the what-if prediction of an average with `predict_marks()` and `what_if()`, and
|
|
85
|
+
`confirm_marks()` for parents.
|
|
86
|
+
- Absence (`bakalari.absence`): `absence()` per day and per subject with the school's percentage threshold.
|
|
87
|
+
- Subjects (`bakalari.subjects`): `subjects()` with the teachers and their contacts, `subject_themes()` (with the
|
|
88
|
+
alternative route for ids a URL path cannot carry) and `find_subject()`.
|
|
89
|
+
- Homeworks (`bakalari.homeworks`): `homeworks()`, `active_homeworks_count()` and `set_homework_done()`.
|
|
90
|
+
- Komens messages (`bakalari.komens`): `messages()` of the received, sent, noticeboard, apology and rating folders
|
|
91
|
+
(optionally only `since` a date), `message()`, `unread_messages_count()`, `mark_message_read()`,
|
|
92
|
+
`confirm_message()`, `hide_message()`, `unhide_message()`, `delete_message()`, `message_types()` (also for a
|
|
93
|
+
reply), `send_message()`, `reply_to_message()`, `upload_attachment()`, `download_attachment()` with
|
|
94
|
+
`AttachmentFile.save()`, and `rating_templates()`. `Message.plain_text` turns the HTML body into readable text.
|
|
95
|
+
- Events (`bakalari.events`): `events()` of the whole school, the user's own or the public ones.
|
|
96
|
+
- GDPR (`bakalari.gdpr`): `consents()`, `child_consents()`, `new_consents_count()` and `gdpr_commissioners()`.
|
|
97
|
+
- Payments (`bakalari.payments`): `class_fund_summary()`, `class_fund_payments()`, `class_fund_payment_info()` with
|
|
98
|
+
the IBAN and the Czech QR payment (SPAYD) string, `regular_payments_summary()`, `account_to_iban()` and
|
|
99
|
+
`format_iban()`.
|
|
100
|
+
- Teacher accounts (`bakalari.teacher`): `classbook()`, `save_classbook_record()`, `lesson_tags()`,
|
|
101
|
+
`lesson_absence()`, `lesson_past()`, `lesson_students_timetable()`, `marking_atoms()`, `marking_marks()` and
|
|
102
|
+
`emergency_configuration()`.
|
|
103
|
+
- Public timetables (`bakalari.public_timetable`, no login): `public_timetable_targets()`, `public_timetable()` and
|
|
104
|
+
`public_timetable_url()` for classes, teachers and rooms, from the newer and the older page layouts, and
|
|
105
|
+
`parse_public_targets()` and `parse_public_timetable()` for saved pages.
|
|
106
|
+
- School directory (`bakalari.schools`, no login): `SchoolDirectory` with `municipalities()`, `schools()` and
|
|
107
|
+
`find()`, which ignores case and diacritics, to find a school's Bakaláři address.
|
|
108
|
+
|
|
109
|
+
#### Command line
|
|
110
|
+
|
|
111
|
+
- The `bakalari` command (also `python -m bakalari`) with 38 commands, grouped by area in `bakalari --help`: public
|
|
112
|
+
data (`version`, `schools`, `public-timetable`), the account and the session (`login`, `logout`, `profile` with the
|
|
113
|
+
alias `whoami`, `modules`, `web-login`, `web-modules`, `at-school`), `timetable`, `substitutions` and `events`,
|
|
114
|
+
marks (`marks`, `final-marks`, `measures`, `predict`, `confirm-marks`), `absence`, `subjects`, `homeworks` and
|
|
115
|
+
`homework-done`, Komens messages (`messages`, `message`, `message-types`, `send-message`, `reply`,
|
|
116
|
+
`confirm-message`, `hide-message`, `delete-message`, `attachment`), `consents`, `commissioners` and `class-fund`,
|
|
117
|
+
teacher accounts (`classbook`, `lesson-tags`, `marking`), and raw requests to any endpoint (`api`).
|
|
118
|
+
- Global options `--base-url`, `--username-env`, `--password-env`, `--session-file` (or `BAKALARI_SESSION_FILE`),
|
|
119
|
+
`--timeout` (30 s), `--retries` (2), `-v` and `--version`. Most commands accept `--json` and then print JSON
|
|
120
|
+
instead of text, and dates are `YYYY-MM-DD`, `today`, `yesterday` or `tomorrow`.
|
|
121
|
+
- The session file keeps the session between commands, so the password is needed only after the refresh token has
|
|
122
|
+
expired. It works without the login variables, which then serve as the fallback for a rejected refresh token.
|
|
123
|
+
- The commands that change data are marked in the help, do exactly what the command line says and print what they
|
|
124
|
+
did. `send-message` checks the command line against the account's message types before it uploads or sends
|
|
125
|
+
anything.
|
|
126
|
+
- Exit status 0 on success, 1 for an API, connection or contract error, with a hint where the cause is likely (e.g.
|
|
127
|
+
a module that is not enabled, or an endpoint for teacher accounts), 2 for a usage or configuration error and 130
|
|
128
|
+
when interrupted. docs/cli.md is the complete reference.
|
|
129
|
+
|
|
130
|
+
#### Package and documentation
|
|
131
|
+
|
|
132
|
+
- The package exports the client, the errors, every model class and the main helpers from `bakalari`, ships
|
|
133
|
+
`py.typed` and has no runtime dependencies.
|
|
134
|
+
- Documentation: the README, the library guide (docs/library.md, every method and model), the command line reference
|
|
135
|
+
(docs/cli.md), the endpoint coverage (docs/endpoints.md) and runnable examples: the weekly timetable, new marks,
|
|
136
|
+
unread messages with their attachments, homeworks as an iCalendar file, a persistent session, the class fund QR
|
|
137
|
+
payment and the school search.
|
|
138
|
+
- An offline test suite with a scripted transport and synthetic fixtures, which CI runs on Python 3.9 to 3.14 on
|
|
139
|
+
Linux and on Python 3.13 on macOS and Windows with at least 90 % coverage, and read-only live tests that run only
|
|
140
|
+
when `BAKALARI_URL`, `BAKALARI_USERNAME` and `BAKALARI_PASSWORD` are set.
|
|
141
|
+
|
|
142
|
+
### Changed
|
|
143
|
+
|
|
144
|
+
- Rename the repository and Python distribution from `bakalari-api` to `bakalari`. Install with
|
|
145
|
+
`python -m pip install bakalari`; the Python import and the `bakalari` and `bakalari-mcp` commands stay the same.
|
|
146
|
+
- Use `bakalari` in CLI version output, MCP server identity, release metadata, documentation and calendar exports,
|
|
147
|
+
and `bakalari-python/<version>` in the default HTTP User-Agent.
|
|
148
|
+
|
|
149
|
+
- The login variables are `BAKALARI_USERNAME` and `BAKALARI_PASSWORD` instead of `BAK_PTRK_USERNAME` and
|
|
150
|
+
`BAK_PTRK_PASSWORD`.
|
|
151
|
+
- The school's address is required: set `BAKALARI_URL` or pass `base_url` (`--base-url` on the command line). There
|
|
152
|
+
is no default school any more.
|
|
153
|
+
- `user_profile()` returns a typed `UserProfile` instead of the raw dict; the server's object is its `raw` field.
|
|
154
|
+
- The timetable models `Timetable`, `Day`, `Lesson`, `Hour` and `Change` are `Model` dataclasses with new fields;
|
|
155
|
+
the existing fields keep their meaning, and `Lesson.to_dict()` keeps its keys and adds `notice`, `week_theme` and
|
|
156
|
+
`homeworks`.
|
|
157
|
+
- `request()` also sends `PUT`, `DELETE` and `PATCH` requests, JSON and raw bodies and extra headers, and accepts a
|
|
158
|
+
set of expected statuses or `None` for any. HTTP 403 and 404 raise `BakalariPermissionError` and
|
|
159
|
+
`BakalariNotFoundError`, which are `BakalariHTTPError` subclasses, so existing handlers keep working.
|
|
160
|
+
- The command line was rewritten; `timetable` keeps its options and output. `bakalari.cli.main()` returns the exit
|
|
161
|
+
status 2 for a usage error instead of letting `SystemExit` escape, and `-v` configures only the package's loggers
|
|
162
|
+
instead of the root logger.
|
|
163
|
+
- Split automation into **Python Package CI** (`python-package.yml`) and **Python Package Publish**
|
|
164
|
+
(`python-publish.yml`), replacing the previous CI and tag-push publishing workflows. Publishing a GitHub
|
|
165
|
+
release, or manually running the publish workflow from its matching tag, validates the release, runs reusable
|
|
166
|
+
CI and publishes its checked distributions through the `pypi` environment using PyPI trusted publishing.
|
|
167
|
+
- Validate that the release tag matches the package version, the changelog has a dated, non-empty release
|
|
168
|
+
section, and no changes remain under "Unreleased" before publishing.
|
|
169
|
+
- Collect both `unittests/` and `tests/` in CI with live tests explicitly disabled, including official MCP SDK
|
|
170
|
+
interoperability and release-validation regression tests. Retain test and coverage reports for 30 days.
|
|
171
|
+
- Check wheel and source-distribution contents, typed-package metadata and installed command entry points;
|
|
172
|
+
keep repository tooling, local state, caches and generated reports out of distributions.
|
|
173
|
+
- Update contributor documentation for the workflow migration, required checks, PyPI trusted-publisher setup
|
|
174
|
+
and release validation.
|
|
175
|
+
|
|
176
|
+
### Fixed
|
|
177
|
+
|
|
178
|
+
- Fall back to the ordinary attachment filename when the server sends a malformed extended filename header.
|
|
179
|
+
- Remove duplicate legacy tests whose missing helper imports broke full test discovery.
|
|
180
|
+
- Pass the coverage configuration explicitly in CI so subprocesses running outside the checkout use the same
|
|
181
|
+
branch-coverage settings and their results can be combined successfully.
|
|
182
|
+
|
|
183
|
+
### Security
|
|
184
|
+
|
|
185
|
+
- Block redirects to another origin for requests carrying an Authorization header or body, including login
|
|
186
|
+
requests, and reject HTTPS downgrades. Public HTTP(S) redirects remain supported.
|
|
187
|
+
- Remove query strings and fragments from request paths in logs, HTTP errors and JSON contract errors, so raw
|
|
188
|
+
request paths cannot expose query tokens in diagnostics.
|
|
189
|
+
- Reject malformed installation URLs and embedded URL credentials before they can appear in diagnostics.
|
|
190
|
+
- Redact known passwords and access/refresh tokens echoed in upstream HTTP error bodies, including nested values.
|
|
191
|
+
- Keep stored passwords and tokens in private attributes and exclude token payloads from `repr()`. Request logs
|
|
192
|
+
omit query strings and bodies, and connection error messages omit query strings that can carry push tokens.
|
|
193
|
+
- TLS certificates are verified by default, and there is no cookie jar, because the API is stateless.
|
|
194
|
+
- The command line writes the session file atomically with mode 0600, restricts a session file that other users
|
|
195
|
+
can read when it loads it (on POSIX systems), refuses a file that is not a Bakaláři session, and `logout`
|
|
196
|
+
deletes nothing but a Bakaláři session file.
|
|
197
|
+
- Text output removes the C0 and C1 control characters from server strings (titles, names, message bodies, file
|
|
198
|
+
names) and JSON output escapes them, so a server cannot send escape sequences to the terminal.
|
|
199
|
+
- `AttachmentFile.save()` and the `attachment` command sanitize the server's file name (no directory parts, control
|
|
200
|
+
characters, Windows device names or hidden files) and never overwrite a file unless asked to.
|
|
201
|
+
- `web-login` warns that its one-time link is a credential. Reading a message never marks it as read. The live tests
|
|
202
|
+
call no endpoint that changes data and never log in with a wrong password for a real user, because Bakaláři shows
|
|
203
|
+
failed logins to the account owner.
|
|
204
|
+
|
|
205
|
+
### Known limitations
|
|
206
|
+
|
|
207
|
+
- The REST API is not officially documented and may change without notice.
|
|
208
|
+
- Not verified against a live server: the teacher endpoints (no teacher account was available), the payments
|
|
209
|
+
payloads (the module was disabled at the test school), the timetable pages of a school that publishes them, and
|
|
210
|
+
the write endpoints (sending, replying, confirming, hiding and deleting messages, uploading attachments,
|
|
211
|
+
confirming marks, ticking homeworks, saving class-book records, registering a push token), which were checked by
|
|
212
|
+
route and method only. Their models follow the community documentation, other clients and captured traffic, and
|
|
213
|
+
keep the server's objects in `raw`.
|
|
214
|
+
- 17 known endpoints have no typed method, mostly because their requests are undocumented (Komens drafts, consent
|
|
215
|
+
answers, emergency alarms, teacher payment prescriptions, class-book attendance writes, the student timetable);
|
|
216
|
+
`request()` and `bakalari api` reach them. Consents can be answered and homeworks handed in electronically on the
|
|
217
|
+
Bakaláři web only.
|
|
218
|
+
- `register_notification()` needs the official app's push token, so a third-party token receives no notifications;
|
|
219
|
+
poll the counters instead. `new_marks_count()` is often 0 even when marks are flagged new.
|
|
220
|
+
- `predict_marks()` takes classic marks only; hypothetical point marks need `what_if()`.
|
|
221
|
+
- `send-message` and `reply` turn plain text into HTML (escaped, line breaks as `<br>`), assuming that the server
|
|
222
|
+
stores messages as HTML like the web editor does; `--html` sends the text unchanged.
|
|
223
|
+
- `class_timetable()` was verified for the account's own class only; the timetable of another class may be refused.
|
|
224
|
+
- Two commands that renew the same session file at the same moment can race, because the file is not locked.
|
|
225
|
+
|
|
226
|
+
[Unreleased]: https://github.com/robco/bakalari/compare/v1.0.0...HEAD
|
|
227
|
+
[1.0.0]: https://github.com/robco/bakalari/releases/tag/v1.0.0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Code of conduct
|
|
2
|
+
|
|
3
|
+
This project adopts the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/)
|
|
4
|
+
as its code of conduct. It applies to everyone who takes part in the project, in issues, pull requests, reviews
|
|
5
|
+
and every other project space, and to anyone who represents the project in public.
|
|
6
|
+
|
|
7
|
+
## In short
|
|
8
|
+
|
|
9
|
+
We want taking part in this project to be a harassment-free experience for everyone. We expect you to:
|
|
10
|
+
|
|
11
|
+
- be respectful and kind, and welcome people of every background and level of experience;
|
|
12
|
+
- give and accept constructive feedback gracefully, and focus on what is best for the project and its users;
|
|
13
|
+
- respect privacy. This project works with the school records of children: never publish anyone's personal data,
|
|
14
|
+
and keep data from a Bakaláři account (names, marks, absences, messages) out of issues, pull requests and
|
|
15
|
+
fixtures.
|
|
16
|
+
|
|
17
|
+
Not acceptable:
|
|
18
|
+
|
|
19
|
+
- harassment, insults, and derogatory or discriminatory comments;
|
|
20
|
+
- personal or political attacks, trolling;
|
|
21
|
+
- sexualized language or imagery, and unwelcome sexual attention;
|
|
22
|
+
- publishing other people's private information without their explicit permission;
|
|
23
|
+
- other conduct that could reasonably be considered inappropriate in a professional setting.
|
|
24
|
+
|
|
25
|
+
## Reporting and enforcement
|
|
26
|
+
|
|
27
|
+
Report unacceptable behaviour privately to the project maintainer, Róbert Malovec, through the repository's
|
|
28
|
+
private reporting form ([Report a vulnerability](https://github.com/robco/bakalari/security/advisories/new))
|
|
29
|
+
with a title that starts with "Code of conduct". Every report is reviewed promptly and fairly, and the reporter's
|
|
30
|
+
privacy is respected.
|
|
31
|
+
|
|
32
|
+
The maintainer enforces this code of conduct following the Contributor Covenant's
|
|
33
|
+
[enforcement guidelines](https://www.contributor-covenant.org/version/2/1/code_of_conduct/#enforcement-guidelines):
|
|
34
|
+
a correction, a warning, a temporary ban or a permanent ban, depending on the impact. Content that breaks GitHub's
|
|
35
|
+
terms can also be
|
|
36
|
+
[reported to GitHub](https://docs.github.com/en/communities/maintaining-your-safety-on-github/reporting-abuse-or-spam).
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Contributing to bakalari
|
|
2
|
+
|
|
3
|
+
Thank you for helping. This guide covers the development setup, the tests, the code conventions and the release
|
|
4
|
+
process. By taking part you agree to follow the [code of conduct](CODE_OF_CONDUCT.md). Report security problems
|
|
5
|
+
privately as described in [SECURITY.md](SECURITY.md), never in a public issue.
|
|
6
|
+
|
|
7
|
+
## Development setup
|
|
8
|
+
|
|
9
|
+
You need Python 3.9 or newer. The package has no runtime dependencies. Test your changes on the oldest supported
|
|
10
|
+
Python (3.9) and on the newest.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
git clone https://github.com/robco/bakalari.git
|
|
14
|
+
cd bakalari
|
|
15
|
+
python3 -m venv .venv
|
|
16
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
17
|
+
python -m pip install -e ".[dev]"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
mypy stays below version 2 because mypy 2 can no longer check code against Python 3.9 (`python_version` in
|
|
21
|
+
`pyproject.toml`).
|
|
22
|
+
|
|
23
|
+
## Tests
|
|
24
|
+
|
|
25
|
+
### Offline tests
|
|
26
|
+
|
|
27
|
+
The offline suite needs no network and no account. It drives the client through a scripted transport
|
|
28
|
+
(`FakeTransport` in `unittests/support.py`) and uses the synthetic fixtures in `unittests/fixtures/`.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
python -m pytest # the whole offline suite
|
|
32
|
+
python -m unittest unittests.test_marks -v # one test module, no test dependencies
|
|
33
|
+
python -m pytest --cov=bakalari --cov-fail-under=90 # CI coverage threshold
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The default pytest discovery path is `unittests/`, so it never selects live tests even when credentials are
|
|
37
|
+
present. Without third-party test tools, run `python -m unittest discover -s unittests -t . -v`.
|
|
38
|
+
|
|
39
|
+
### MCP SDK interoperability
|
|
40
|
+
|
|
41
|
+
On Python 3.10 or newer, install the optional reference client and run the offline interoperability test:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
python -m pip install -e ".[dev,mcp-test]"
|
|
45
|
+
python -m pytest tests/test_mcp_sdk.py
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
CI runs this test alongside the unit tests under coverage. It uses synthetic school responses and requires no
|
|
49
|
+
account. The installed library and MCP server still have no runtime dependencies.
|
|
50
|
+
|
|
51
|
+
### Live tests (read-only)
|
|
52
|
+
|
|
53
|
+
`tests/test_live.py` runs against a real Bakaláři installation when all three variables are set and
|
|
54
|
+
`BAKALARI_SKIP_LIVE` is not:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
export BAKALARI_URL=https://... # the address of your school's Bakaláři web
|
|
58
|
+
export BAKALARI_USERNAME=...
|
|
59
|
+
export BAKALARI_PASSWORD=...
|
|
60
|
+
python -m unittest tests.test_live -v
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Set `BAKALARI_SKIP_LIVE=1` to skip them even when the credentials are set. CI never runs them. The rules for
|
|
64
|
+
live tests:
|
|
65
|
+
|
|
66
|
+
- **They never change data.** No marking messages as read, no sending messages, no confirming marks, no marking
|
|
67
|
+
homework as done, no consent changes, no notification registration: only read endpoints.
|
|
68
|
+
- **They never use a wrong password with a real username.** Bakaláři counts failed logins and shows them to the
|
|
69
|
+
account owner at the next login. The negative login test uses a random, unknown username.
|
|
70
|
+
- They assert structure only (types, non-negative counters, parsing with validation on), never concrete personal
|
|
71
|
+
values, and they skip (`skipTest`) when the school or account has a module disabled (`BakalariPermissionError`).
|
|
72
|
+
- Their output can contain personal data. Don't paste it into issues or pull requests unless it's redacted.
|
|
73
|
+
|
|
74
|
+
## Fixture privacy rule
|
|
75
|
+
|
|
76
|
+
Fixtures are synthetic. Copy the **shape** of a real response (keys, nesting, value types, enum values,
|
|
77
|
+
space-padded ids, empty and null fields) but never its values. Invent the names, ids, texts, marks and dates, and
|
|
78
|
+
use `https://school.example/bakaweb` as the address. Never commit a real name, id, school, message text or token,
|
|
79
|
+
not even from your own account, and never commit a captured response with the plan to anonymize it later.
|
|
80
|
+
|
|
81
|
+
## Code conventions
|
|
82
|
+
|
|
83
|
+
The package uses only the standard library and runs on Python 3.9. Keep `from __future__ import annotations` and
|
|
84
|
+
the `typing` style (`Optional`, `Dict`, `List`, `Tuple`, `Mapping`). Don't use `match`, `X | Y` at runtime,
|
|
85
|
+
`dataclass(slots=..., kw_only=...)`, `zip(strict=...)` or `datetime.UTC`.
|
|
86
|
+
|
|
87
|
+
1. **One module per Bakaláři area** (`marks.py`, `komens.py`, ...) holds the area's response models and its
|
|
88
|
+
`XxxEndpoints(ClientCore)` mixin, which `BakalariClient` combines. Endpoint methods are thin: they build the
|
|
89
|
+
path (ids go through `quote_segment`), call `get_json`, `post_json` or `request`, and parse the result with the
|
|
90
|
+
model's `from_json`.
|
|
91
|
+
2. **Models** are `@dataclass(frozen=True)` subclasses of `Model` with a `from_json` classmethod. Entity models end
|
|
92
|
+
with a `raw` field holding the original object, which is hidden from `repr()` and `to_dict()`. Field names are
|
|
93
|
+
the snake_case API names (`school_class` for `Class`), and collections are tuples.
|
|
94
|
+
3. **Values** go through the `bakalari._parse` helpers: ids exact (`ident`), display strings trimmed (`text`),
|
|
95
|
+
timestamps as aware `datetime` (`parse_datetime`) or `date` (`parse_date`), numbers and booleans via
|
|
96
|
+
`to_int`, `to_float` and `to_bool`, `{Id, Abbrev, Name}` objects as `Item`, and attachments as
|
|
97
|
+
`AttachmentInfo`. Parsing is lenient: a missing optional field gets a default.
|
|
98
|
+
4. **Contract checks**: a top-level `from_json(data, *, validate=True)` checks the essential shape with
|
|
99
|
+
`check_shape` when it validates, and with `require_mapping` or `require_list` always. Endpoint methods take
|
|
100
|
+
`validate: bool = True` and pass it on. Counter endpoints use `require_int`.
|
|
101
|
+
5. **Dates in requests** are formatted with `format_date`. Date parameters are named `since` and `until` and
|
|
102
|
+
accept a `date`, a `datetime` or an ISO string.
|
|
103
|
+
6. **Docstrings** in English on every public class and method: one or two sentences naming the HTTP method and
|
|
104
|
+
path (``GET /api/3/marks``) and notable server behaviour. Comments only explain non-obvious server quirks.
|
|
105
|
+
7. **Errors** are never swallowed. Endpoints that need a module or a right let `BakalariPermissionError`
|
|
106
|
+
propagate. Tokens and passwords never appear in messages, log records or `repr()`.
|
|
107
|
+
8. **Tests** are offline and deterministic (`unittest`), one test module per area module (`unittests/test_marks.py`),
|
|
108
|
+
with synthetic fixtures `unittests/fixtures/<area>*.json`. They cover model parsing (padded ids, nulls, missing
|
|
109
|
+
fields), `to_dict()` JSON round trips, the HTTP method, path (including percent-encoded ids), query and body of
|
|
110
|
+
every endpoint method, status handling, contract errors and lenient parsing.
|
|
111
|
+
|
|
112
|
+
## Lint and type check
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
ruff check . # pycodestyle, pyflakes, isort, bugbear and comprehensions; line length 120
|
|
116
|
+
mypy bakalari # the settings in pyproject.toml, checked against Python 3.9 (needs mypy < 2)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Ruff's pyupgrade (`UP`) rules are off on purpose, so that the `Optional[...]` typing style stays.
|
|
120
|
+
|
|
121
|
+
To check the packaging the way CI does:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
python -m build
|
|
125
|
+
twine check --strict dist/*
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Pull requests
|
|
129
|
+
|
|
130
|
+
Keep a pull request focused on one change, add tests, update the documentation and add an entry to
|
|
131
|
+
`CHANGELOG.md` under "Unreleased". CI runs the offline tests on Python 3.9 to 3.14 on Linux and on Python 3.13 on
|
|
132
|
+
macOS and Windows, checks coverage (at least 90 %), runs ruff and mypy, and builds the package, checking the wheel
|
|
133
|
+
by installing it into a fresh virtual environment. Python 3.12 runs the combined test/build job, which also
|
|
134
|
+
checks the MCP SDK and uploads JUnit, HTML and coverage reports. The remaining matrix jobs use only the standard
|
|
135
|
+
library to check compatibility.
|
|
136
|
+
|
|
137
|
+
## Release process
|
|
138
|
+
|
|
139
|
+
Versions follow [Semantic Versioning](https://semver.org/). The version is set in one place,
|
|
140
|
+
`bakalari/_version.py`. Setuptools reads that value for the distribution, and the library, CLI and MCP server
|
|
141
|
+
use it at runtime. The filename is a project convention, not a GitHub Actions or PyPI requirement
|
|
142
|
+
|
|
143
|
+
1. Set `__version__ = "X.Y.Z"` in `bakalari/_version.py`.
|
|
144
|
+
2. In `CHANGELOG.md`, rename "Unreleased" to `## [X.Y.Z] - YYYY-MM-DD`, add a new empty "Unreleased"
|
|
145
|
+
section above it, and update the link references at the bottom.
|
|
146
|
+
3. Commit the changes, push to `main`, and wait for **Python Package CI** to pass.
|
|
147
|
+
4. Create and push the release tag:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
git tag -a vX.Y.Z -m "bakalari X.Y.Z"
|
|
151
|
+
git push origin vX.Y.Z
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
5. On GitHub, publish a release for that tag and use the changelog section as its release notes. The
|
|
155
|
+
[Python Package Publish workflow](.github/workflows/python-publish.yml) starts on `release: published`.
|
|
156
|
+
It builds the wheel and source distribution, uploads them as `release-dists`, then publishes them to PyPI.
|
|
157
|
+
A tag push alone does not publish a package.
|
|
158
|
+
|
|
159
|
+
Publishing does not rerun CI or enforce tag naming or changelog formatting. The package version comes from
|
|
160
|
+
`bakalari/_version.py` in the selected checkout, regardless of the branch, tag or release name.
|
|
161
|
+
CI runs tests, lint, type and distribution checks separately; its `python-package` artifact and test and
|
|
162
|
+
coverage reports are retained for 30 days. The publish job uses the `pypi` environment and OpenID Connect
|
|
163
|
+
trusted publishing to upload the distributions from its own build job.
|
|
164
|
+
|
|
165
|
+
To publish manually, select a branch or tag in GitHub's **Run workflow** dialog, or use:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
gh workflow run python-publish.yml --ref main
|
|
169
|
+
# Or publish a specific tag:
|
|
170
|
+
gh workflow run python-publish.yml --ref vX.Y.Z
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
A manual run also publishes to **PyPI**. PyPI accepts each distribution filename only once; bump the package
|
|
174
|
+
version before a new release and check a partially completed upload before retrying. For a local build
|
|
175
|
+
check without publishing, use `python -m build`, `python -m twine check --strict dist/*` and
|
|
176
|
+
`python .github/scripts/check_distribution.py`.
|
|
177
|
+
|
|
178
|
+
### First-time setup and migration
|
|
179
|
+
|
|
180
|
+
1. On GitHub, create the `pypi` environment (Settings, Environments). Allow the branches and tags you intend
|
|
181
|
+
to publish from; remove any tag-only restriction if you want manual publishing from `main`.
|
|
182
|
+
2. Configure a [PyPI trusted publisher](https://docs.pypi.org/trusted-publishers/adding-a-publisher/):
|
|
183
|
+
- PyPI project: `bakalari`
|
|
184
|
+
- Owner: `robco`
|
|
185
|
+
- Repository: `bakalari`
|
|
186
|
+
- Workflow filename: `python-publish.yml`
|
|
187
|
+
- Environment: `pypi`
|
|
188
|
+
|
|
189
|
+
If the project does not exist, add a pending publisher in the account's Publishing settings. No API token
|
|
190
|
+
is needed. If migrating from the previous workflow, replace the publisher for `release.yml` with the one
|
|
191
|
+
above; PyPI matches the workflow filename exactly.
|
|
192
|
+
3. If branch protection requires the old CI job names, update those required checks to the checks reported by
|
|
193
|
+
**Python Package CI**, including the compatibility matrix and **Test & Build (Python 3.12)**.
|
|
194
|
+
4. The old tag-push publisher and manual TestPyPI rehearsal are replaced by this release-based workflow.
|
|
195
|
+
The `testpypi` environment and its publisher are no longer used.
|
|
196
|
+
5. Enable private vulnerability reporting (Settings, Advanced Security, Private vulnerability reporting), which
|
|
197
|
+
SECURITY.md and the issue forms link to.
|
bakalari-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Róbert Malovec
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Source distribution contents in addition to what setuptools adds by itself
|
|
2
|
+
# (the bakalari package with py.typed, README.md, LICENSE and pyproject.toml).
|
|
3
|
+
include CHANGELOG.md CONTRIBUTING.md SECURITY.md CODE_OF_CONDUCT.md
|
|
4
|
+
graft unittests
|
|
5
|
+
graft tests
|
|
6
|
+
graft docs
|
|
7
|
+
graft examples
|
|
8
|
+
|
|
9
|
+
# Never ship repository tooling, local agent state, build output, caches or secrets.
|
|
10
|
+
prune .github
|
|
11
|
+
prune .claude
|
|
12
|
+
prune build
|
|
13
|
+
prune dist
|
|
14
|
+
prune htmlcov
|
|
15
|
+
prune .mypy_cache
|
|
16
|
+
prune .ruff_cache
|
|
17
|
+
prune .pytest_cache
|
|
18
|
+
prune reports
|
|
19
|
+
global-exclude *.py[cod] .DS_Store .env *.session.json
|