evesde 0.1.0a1__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.
evesde-0.1.0a1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 evesde contributors
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,256 @@
1
+ Metadata-Version: 2.4
2
+ Name: evesde
3
+ Version: 0.1.0a1
4
+ Summary: Библиотека для работы со статическими данными EVE Online (SDE)
5
+ Author: AlexMidRus
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Alexmidrus/Eve_SDE
8
+ Project-URL: Repository, https://github.com/Alexmidrus/Eve_SDE
9
+ Project-URL: Issues, https://github.com/Alexmidrus/Eve_SDE/issues
10
+ Keywords: eve-online,eve,sde,static-data-export,sqlalchemy
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Games/Entertainment
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: sqlalchemy>=2.0
27
+ Requires-Dist: click>=8.1
28
+ Requires-Dist: httpx>=0.27
29
+ Provides-Extra: postgres
30
+ Requires-Dist: psycopg[binary]>=3.1; extra == "postgres"
31
+ Provides-Extra: mysql
32
+ Requires-Dist: pymysql>=1.1; extra == "mysql"
33
+ Provides-Extra: dev
34
+ Requires-Dist: pytest>=8.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.5; extra == "dev"
36
+ Requires-Dist: mypy>=1.10; extra == "dev"
37
+ Requires-Dist: apscheduler>=3.10; extra == "dev"
38
+ Requires-Dist: celery>=5.3; extra == "dev"
39
+ Provides-Extra: release
40
+ Requires-Dist: build>=1.0; extra == "release"
41
+ Requires-Dist: twine>=5.0; extra == "release"
42
+ Dynamic: license-file
43
+
44
+ # evesde
45
+
46
+ Библиотека для работы со статическими данными EVE Online (SDE): загрузка
47
+ официального JSONL-дампа CCP в SQLite/PostgreSQL/MySQL/MariaDB и лаконичный
48
+ API для чтения игровых данных (~10 методов с богатыми параметрами вместо
49
+ десятков узких).
50
+
51
+ ## English
52
+
53
+ `evesde` is a Python library for working with EVE Online's Static Data
54
+ Export (SDE): it loads CCP's official JSONL dump into SQLite/PostgreSQL/
55
+ MySQL/MariaDB and exposes a small, richly-parameterized query API (~10
56
+ methods) instead of dozens of narrow ones. Install with
57
+ `pip install evesde` (add `[postgres]`/`[mysql]` extras for those drivers),
58
+ then:
59
+
60
+ ```python
61
+ from evesde import SDE
62
+
63
+ sde = SDE("sqlite:///eve.db")
64
+ sde.update_if_needed() # downloads and loads SDE on first run
65
+ ship = sde.item("Rifter") # by name or type_id, any of the 8 SDE languages
66
+ ```
67
+
68
+ The rest of this README is in Russian; see the docstrings on `evesde.SDE`
69
+ for full per-method reference in English.
70
+
71
+ ## Установка
72
+
73
+ ```bash
74
+ pip install evesde
75
+ # с драйвером для конкретной СУБД:
76
+ pip install "evesde[postgres]"
77
+ pip install "evesde[mysql]"
78
+ ```
79
+
80
+ ## Быстрый старт
81
+
82
+ ```python
83
+ from evesde import SDE
84
+
85
+ sde = SDE("sqlite:///eve.db")
86
+ sde.update_if_needed() # скачает и загрузит SDE при первом запуске
87
+
88
+ ship = sde.item("Rifter") # по имени или type_id, любой из 8 языков SDE
89
+ print(ship.name, ship.group_name, ship.category_name)
90
+ ```
91
+
92
+ Если SDE уже загружен локальным каталогом (например, командой `evesde load`),
93
+ `update_if_needed()` можно не вызывать каждый раз — она сама проверяет версию
94
+ у CCP и ничего не делает, если данные уже актуальны.
95
+
96
+ ## Публичный API
97
+
98
+ Все методы — на объекте `SDE`; принимают `lang="en"` (любой из 8 языков SDE:
99
+ `de/en/es/fr/ja/ko/ru/zh`) и возвращают лёгкие frozen dataclass'ы. Разрешение
100
+ "имя или id" единообразно: `int` — это `id`, `str` ищется точным совпадением
101
+ по локализованному имени, затем по подстроке; неоднозначность бросает
102
+ `SDEAmbiguousNameError` (со списком кандидатов в `.candidates`), отсутствие —
103
+ `SDENotFoundError`. Всё, что не покрыто API, доступно через `sde.engine`
104
+ (SQLAlchemy Core).
105
+
106
+ | Метод | Параметры | Возвращает | Пример |
107
+ |---|---|---|---|
108
+ | `item` | `name_or_id`, `lang` | `Item` | `sde.item("Rifter")` |
109
+ | `items` | `group`, `category`, `market_group`, `published`, `lang` | `list[Item]` | `sde.items(category="Ship", published=True)` |
110
+ | `search` | `name`, `lang`, `limit` | `list[Item]` | `sde.search("Rift")` |
111
+ | `stats` | `type_id_or_name`, `lang` | `TypeStats` | `sde.stats("Rifter").high_slots` |
112
+ | `dogma` | `type_id_or_name`, `lang` | `list[DogmaAttribute]` | `sde.dogma("Rifter")` |
113
+ | `system` | `name_or_id`, `lang` | `SolarSystem` | `sde.system("Jita").region_name` |
114
+ | `systems` | `region`, `min_security`, `max_security`, `lang` | `list[SolarSystem]` | `sde.systems(region="The Forge", min_security=0.5)` |
115
+ | `industry` | `blueprint` или `product`, `activity`, `lang` | `list[IndustryRecipe]` | `sde.industry(product="Rifter", activity="manufacturing")` |
116
+ | `agents` | `level`, `region`, `is_locator`, `corporation`, `lang` | `list[Agent]` | `sde.agents(level=4, region="The Forge", is_locator=True)` |
117
+ | `meta` | — | `Meta` | `sde.meta().build_number` |
118
+ | `update_if_needed` | `force`, `on_progress`, `on_complete` | `UpdateResult` | `sde.update_if_needed()` |
119
+
120
+ `industry()` нужен `blueprint` (сам чертёж) либо `product` (что он
121
+ производит); без `activity` возвращаются рецепты всех активностей чертежа
122
+ (`manufacturing`, `copying`, `invention`, `reaction`, `research_material`,
123
+ `research_time`), с `activity` — только она.
124
+
125
+ ## Команды CLI
126
+
127
+ ```bash
128
+ evesde --db sqlite:///eve.db load ./sde_dump # загрузить из локального каталога
129
+ evesde --db sqlite:///eve.db load --download # скачать и загрузить
130
+ evesde --db sqlite:///eve.db update # обновить, если вышел новый build
131
+ evesde --db sqlite:///eve.db verify # проверить целостность
132
+ evesde --db sqlite:///eve.db status # версия, число таблиц/строк, размер БД
133
+ ```
134
+
135
+ Вместо `--db <url>` можно задать переменную окружения `EVESDE_DB`. Коды
136
+ возврата: `0` — успех, `1` — ошибка выполнения, `2` — `verify` нашёл ошибки
137
+ (предупреждения код возврата не меняют).
138
+
139
+ ## Расписание обновлений
140
+
141
+ Библиотека не содержит встроенного планировщика: `SDE.update_if_needed()`
142
+ (сверяет версию у CCP и, если вышел новый build, атомарно перезагружает БД)
143
+ и `evesde update` (то же самое из CLI) — это просто функции, которые ваше
144
+ приложение вызывает по расписанию любым удобным способом.
145
+
146
+ ### cron + CLI
147
+
148
+ ```bash
149
+ # каждый день в 04:00 проверить версию у CCP и обновить БД при необходимости
150
+ 0 4 * * * EVESDE_DB=sqlite:////srv/app/eve.db evesde update >> /var/log/evesde-update.log 2>&1
151
+ ```
152
+
153
+ ### APScheduler
154
+
155
+ ```python
156
+ from apscheduler.schedulers.blocking import BlockingScheduler
157
+
158
+ from evesde import SDE
159
+
160
+ sde = SDE("sqlite:///eve.db")
161
+ scheduler = BlockingScheduler()
162
+
163
+
164
+ @scheduler.scheduled_job("interval", hours=6)
165
+ def update_sde() -> None:
166
+ result = sde.update_if_needed(on_progress=print)
167
+ if result.updated:
168
+ print(f"SDE обновлён до build {result.new_build.build_number}")
169
+
170
+
171
+ scheduler.start()
172
+ ```
173
+
174
+ ### Celery beat
175
+
176
+ ```python
177
+ from celery import Celery
178
+
179
+ from evesde import SDE
180
+
181
+ app = Celery("myapp", broker="redis://localhost:6379/0")
182
+ sde = SDE("sqlite:///eve.db")
183
+
184
+
185
+ @app.task
186
+ def update_sde() -> None:
187
+ sde.update_if_needed()
188
+
189
+
190
+ app.conf.beat_schedule = {
191
+ "update-sde-every-6-hours": {
192
+ "task": "myapp.tasks.update_sde",
193
+ "schedule": 6 * 60 * 60,
194
+ },
195
+ }
196
+ ```
197
+
198
+ ## Схема данных
199
+
200
+ Три слоя (полный список из 182 таблиц, колонки и связи — см.
201
+ `reference/SDE_schema_report.md`):
202
+
203
+ 1. **raw-слой** (174 таблицы) — 1 исходный `.jsonl`-файл = 1 корневая
204
+ таблица + дочерние таблицы для вложенных массивов, без потерь и
205
+ нормализации справочников.
206
+ 2. **Витрины** (8 таблиц: `dim_items`, `dim_universe`, `dim_agents`,
207
+ `type_common_stats`, `industry_activities/materials/products/skills`) —
208
+ денормализованные таблицы поверх raw-слоя, физически материализуются
209
+ заново после каждой загрузки (`INSERT ... SELECT`, не VIEW).
210
+ 3. Публичный API читает в первую очередь витрины; raw-слой — для редких
211
+ полей и произвольных запросов через `sde.engine`.
212
+
213
+ ## Ограничения и особенности данных
214
+
215
+ - `types.faction_id` — смешанная ссылка: часть значений (~10 из 33) на
216
+ самом деле ID NPC-корпораций, а не фракций. `dim_items.faction_name`
217
+ в этих случаях будет `None` — это ожидаемое поведение, а не баг.
218
+ - `agents_in_space.dungeon_id` не совпадает с `dungeons._key` — внешний
219
+ ключ на эту колонку сознательно не строится.
220
+ - Не у каждого предмета есть dogma-атрибуты (`typeDogma` заполнена не для
221
+ всех типов): `stats()` бросает `SDENotFoundError`, если характеристик
222
+ нет, `dogma()` в этом случае просто возвращает пустой список.
223
+ - Значения dogma-атрибутов в исходном SDE всегда `FLOAT`; там, где смысл
224
+ атрибута целочисленный (слоты, уровни скиллов), `type_common_stats`
225
+ приводит их к `INTEGER` (например, `stats(...).high_slots`).
226
+
227
+ ## Как запускать тесты
228
+
229
+ Быстрый набор (SQLite in-memory, всегда) не требует ничего, кроме
230
+ dev-зависимостей:
231
+
232
+ ```bash
233
+ pip install -e ".[dev]"
234
+ pytest # включает dbms-тесты, только если СУБД доступна
235
+ pytest -m "not dbms" # явно только быстрый набор
236
+ ```
237
+
238
+ Интеграционные тесты на реальных PostgreSQL и MariaDB (маркер `dbms`)
239
+ поднимаются через `docker-compose.test.yml`:
240
+
241
+ ```bash
242
+ docker compose -f docker-compose.test.yml up -d
243
+ pip install -e ".[dev,postgres,mysql]"
244
+
245
+ export EVESDE_TEST_POSTGRES_URL="postgresql+psycopg://evesde:evesde@localhost:5432/evesde_test"
246
+ export EVESDE_TEST_MYSQL_URL="mysql+pymysql://evesde:evesde@localhost:3306/evesde_test"
247
+ pytest -m dbms
248
+
249
+ docker compose -f docker-compose.test.yml down -v
250
+ ```
251
+
252
+ Без этих переменных (или если СУБД недоступна) `dbms`-тесты аккуратно
253
+ пропускаются (skip), а не падают -- обычный `pytest` без docker всегда
254
+ зелёный. В CI (`.github/workflows/ci.yml`) есть отдельные джобы `lint`
255
+ (ruff + mypy), `test-sqlite` (матрица версий Python) и `test-dbms`
256
+ (PostgreSQL + MariaDB как сервисы).
@@ -0,0 +1,213 @@
1
+ # evesde
2
+
3
+ Библиотека для работы со статическими данными EVE Online (SDE): загрузка
4
+ официального JSONL-дампа CCP в SQLite/PostgreSQL/MySQL/MariaDB и лаконичный
5
+ API для чтения игровых данных (~10 методов с богатыми параметрами вместо
6
+ десятков узких).
7
+
8
+ ## English
9
+
10
+ `evesde` is a Python library for working with EVE Online's Static Data
11
+ Export (SDE): it loads CCP's official JSONL dump into SQLite/PostgreSQL/
12
+ MySQL/MariaDB and exposes a small, richly-parameterized query API (~10
13
+ methods) instead of dozens of narrow ones. Install with
14
+ `pip install evesde` (add `[postgres]`/`[mysql]` extras for those drivers),
15
+ then:
16
+
17
+ ```python
18
+ from evesde import SDE
19
+
20
+ sde = SDE("sqlite:///eve.db")
21
+ sde.update_if_needed() # downloads and loads SDE on first run
22
+ ship = sde.item("Rifter") # by name or type_id, any of the 8 SDE languages
23
+ ```
24
+
25
+ The rest of this README is in Russian; see the docstrings on `evesde.SDE`
26
+ for full per-method reference in English.
27
+
28
+ ## Установка
29
+
30
+ ```bash
31
+ pip install evesde
32
+ # с драйвером для конкретной СУБД:
33
+ pip install "evesde[postgres]"
34
+ pip install "evesde[mysql]"
35
+ ```
36
+
37
+ ## Быстрый старт
38
+
39
+ ```python
40
+ from evesde import SDE
41
+
42
+ sde = SDE("sqlite:///eve.db")
43
+ sde.update_if_needed() # скачает и загрузит SDE при первом запуске
44
+
45
+ ship = sde.item("Rifter") # по имени или type_id, любой из 8 языков SDE
46
+ print(ship.name, ship.group_name, ship.category_name)
47
+ ```
48
+
49
+ Если SDE уже загружен локальным каталогом (например, командой `evesde load`),
50
+ `update_if_needed()` можно не вызывать каждый раз — она сама проверяет версию
51
+ у CCP и ничего не делает, если данные уже актуальны.
52
+
53
+ ## Публичный API
54
+
55
+ Все методы — на объекте `SDE`; принимают `lang="en"` (любой из 8 языков SDE:
56
+ `de/en/es/fr/ja/ko/ru/zh`) и возвращают лёгкие frozen dataclass'ы. Разрешение
57
+ "имя или id" единообразно: `int` — это `id`, `str` ищется точным совпадением
58
+ по локализованному имени, затем по подстроке; неоднозначность бросает
59
+ `SDEAmbiguousNameError` (со списком кандидатов в `.candidates`), отсутствие —
60
+ `SDENotFoundError`. Всё, что не покрыто API, доступно через `sde.engine`
61
+ (SQLAlchemy Core).
62
+
63
+ | Метод | Параметры | Возвращает | Пример |
64
+ |---|---|---|---|
65
+ | `item` | `name_or_id`, `lang` | `Item` | `sde.item("Rifter")` |
66
+ | `items` | `group`, `category`, `market_group`, `published`, `lang` | `list[Item]` | `sde.items(category="Ship", published=True)` |
67
+ | `search` | `name`, `lang`, `limit` | `list[Item]` | `sde.search("Rift")` |
68
+ | `stats` | `type_id_or_name`, `lang` | `TypeStats` | `sde.stats("Rifter").high_slots` |
69
+ | `dogma` | `type_id_or_name`, `lang` | `list[DogmaAttribute]` | `sde.dogma("Rifter")` |
70
+ | `system` | `name_or_id`, `lang` | `SolarSystem` | `sde.system("Jita").region_name` |
71
+ | `systems` | `region`, `min_security`, `max_security`, `lang` | `list[SolarSystem]` | `sde.systems(region="The Forge", min_security=0.5)` |
72
+ | `industry` | `blueprint` или `product`, `activity`, `lang` | `list[IndustryRecipe]` | `sde.industry(product="Rifter", activity="manufacturing")` |
73
+ | `agents` | `level`, `region`, `is_locator`, `corporation`, `lang` | `list[Agent]` | `sde.agents(level=4, region="The Forge", is_locator=True)` |
74
+ | `meta` | — | `Meta` | `sde.meta().build_number` |
75
+ | `update_if_needed` | `force`, `on_progress`, `on_complete` | `UpdateResult` | `sde.update_if_needed()` |
76
+
77
+ `industry()` нужен `blueprint` (сам чертёж) либо `product` (что он
78
+ производит); без `activity` возвращаются рецепты всех активностей чертежа
79
+ (`manufacturing`, `copying`, `invention`, `reaction`, `research_material`,
80
+ `research_time`), с `activity` — только она.
81
+
82
+ ## Команды CLI
83
+
84
+ ```bash
85
+ evesde --db sqlite:///eve.db load ./sde_dump # загрузить из локального каталога
86
+ evesde --db sqlite:///eve.db load --download # скачать и загрузить
87
+ evesde --db sqlite:///eve.db update # обновить, если вышел новый build
88
+ evesde --db sqlite:///eve.db verify # проверить целостность
89
+ evesde --db sqlite:///eve.db status # версия, число таблиц/строк, размер БД
90
+ ```
91
+
92
+ Вместо `--db <url>` можно задать переменную окружения `EVESDE_DB`. Коды
93
+ возврата: `0` — успех, `1` — ошибка выполнения, `2` — `verify` нашёл ошибки
94
+ (предупреждения код возврата не меняют).
95
+
96
+ ## Расписание обновлений
97
+
98
+ Библиотека не содержит встроенного планировщика: `SDE.update_if_needed()`
99
+ (сверяет версию у CCP и, если вышел новый build, атомарно перезагружает БД)
100
+ и `evesde update` (то же самое из CLI) — это просто функции, которые ваше
101
+ приложение вызывает по расписанию любым удобным способом.
102
+
103
+ ### cron + CLI
104
+
105
+ ```bash
106
+ # каждый день в 04:00 проверить версию у CCP и обновить БД при необходимости
107
+ 0 4 * * * EVESDE_DB=sqlite:////srv/app/eve.db evesde update >> /var/log/evesde-update.log 2>&1
108
+ ```
109
+
110
+ ### APScheduler
111
+
112
+ ```python
113
+ from apscheduler.schedulers.blocking import BlockingScheduler
114
+
115
+ from evesde import SDE
116
+
117
+ sde = SDE("sqlite:///eve.db")
118
+ scheduler = BlockingScheduler()
119
+
120
+
121
+ @scheduler.scheduled_job("interval", hours=6)
122
+ def update_sde() -> None:
123
+ result = sde.update_if_needed(on_progress=print)
124
+ if result.updated:
125
+ print(f"SDE обновлён до build {result.new_build.build_number}")
126
+
127
+
128
+ scheduler.start()
129
+ ```
130
+
131
+ ### Celery beat
132
+
133
+ ```python
134
+ from celery import Celery
135
+
136
+ from evesde import SDE
137
+
138
+ app = Celery("myapp", broker="redis://localhost:6379/0")
139
+ sde = SDE("sqlite:///eve.db")
140
+
141
+
142
+ @app.task
143
+ def update_sde() -> None:
144
+ sde.update_if_needed()
145
+
146
+
147
+ app.conf.beat_schedule = {
148
+ "update-sde-every-6-hours": {
149
+ "task": "myapp.tasks.update_sde",
150
+ "schedule": 6 * 60 * 60,
151
+ },
152
+ }
153
+ ```
154
+
155
+ ## Схема данных
156
+
157
+ Три слоя (полный список из 182 таблиц, колонки и связи — см.
158
+ `reference/SDE_schema_report.md`):
159
+
160
+ 1. **raw-слой** (174 таблицы) — 1 исходный `.jsonl`-файл = 1 корневая
161
+ таблица + дочерние таблицы для вложенных массивов, без потерь и
162
+ нормализации справочников.
163
+ 2. **Витрины** (8 таблиц: `dim_items`, `dim_universe`, `dim_agents`,
164
+ `type_common_stats`, `industry_activities/materials/products/skills`) —
165
+ денормализованные таблицы поверх raw-слоя, физически материализуются
166
+ заново после каждой загрузки (`INSERT ... SELECT`, не VIEW).
167
+ 3. Публичный API читает в первую очередь витрины; raw-слой — для редких
168
+ полей и произвольных запросов через `sde.engine`.
169
+
170
+ ## Ограничения и особенности данных
171
+
172
+ - `types.faction_id` — смешанная ссылка: часть значений (~10 из 33) на
173
+ самом деле ID NPC-корпораций, а не фракций. `dim_items.faction_name`
174
+ в этих случаях будет `None` — это ожидаемое поведение, а не баг.
175
+ - `agents_in_space.dungeon_id` не совпадает с `dungeons._key` — внешний
176
+ ключ на эту колонку сознательно не строится.
177
+ - Не у каждого предмета есть dogma-атрибуты (`typeDogma` заполнена не для
178
+ всех типов): `stats()` бросает `SDENotFoundError`, если характеристик
179
+ нет, `dogma()` в этом случае просто возвращает пустой список.
180
+ - Значения dogma-атрибутов в исходном SDE всегда `FLOAT`; там, где смысл
181
+ атрибута целочисленный (слоты, уровни скиллов), `type_common_stats`
182
+ приводит их к `INTEGER` (например, `stats(...).high_slots`).
183
+
184
+ ## Как запускать тесты
185
+
186
+ Быстрый набор (SQLite in-memory, всегда) не требует ничего, кроме
187
+ dev-зависимостей:
188
+
189
+ ```bash
190
+ pip install -e ".[dev]"
191
+ pytest # включает dbms-тесты, только если СУБД доступна
192
+ pytest -m "not dbms" # явно только быстрый набор
193
+ ```
194
+
195
+ Интеграционные тесты на реальных PostgreSQL и MariaDB (маркер `dbms`)
196
+ поднимаются через `docker-compose.test.yml`:
197
+
198
+ ```bash
199
+ docker compose -f docker-compose.test.yml up -d
200
+ pip install -e ".[dev,postgres,mysql]"
201
+
202
+ export EVESDE_TEST_POSTGRES_URL="postgresql+psycopg://evesde:evesde@localhost:5432/evesde_test"
203
+ export EVESDE_TEST_MYSQL_URL="mysql+pymysql://evesde:evesde@localhost:3306/evesde_test"
204
+ pytest -m dbms
205
+
206
+ docker compose -f docker-compose.test.yml down -v
207
+ ```
208
+
209
+ Без этих переменных (или если СУБД недоступна) `dbms`-тесты аккуратно
210
+ пропускаются (skip), а не падают -- обычный `pytest` без docker всегда
211
+ зелёный. В CI (`.github/workflows/ci.yml`) есть отдельные джобы `lint`
212
+ (ruff + mypy), `test-sqlite` (матрица версий Python) и `test-dbms`
213
+ (PostgreSQL + MariaDB как сервисы).
@@ -0,0 +1,91 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "evesde"
7
+ version = "0.1.0a1"
8
+ description = "Библиотека для работы со статическими данными EVE Online (SDE)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "AlexMidRus" }]
13
+ keywords = ["eve-online", "eve", "sde", "static-data-export", "sqlalchemy"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Games/Entertainment",
25
+ "Topic :: Software Development :: Libraries :: Python Modules",
26
+ "Typing :: Typed",
27
+ ]
28
+ dependencies = [
29
+ "sqlalchemy>=2.0",
30
+ "click>=8.1",
31
+ "httpx>=0.27",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/Alexmidrus/Eve_SDE"
36
+ Repository = "https://github.com/Alexmidrus/Eve_SDE"
37
+ Issues = "https://github.com/Alexmidrus/Eve_SDE/issues"
38
+
39
+ [project.scripts]
40
+ evesde = "evesde.cli:cli"
41
+
42
+ [project.optional-dependencies]
43
+ postgres = ["psycopg[binary]>=3.1"]
44
+ mysql = ["pymysql>=1.1"]
45
+ dev = [
46
+ "pytest>=8.0",
47
+ "ruff>=0.5",
48
+ "mypy>=1.10",
49
+ # Только для теста, что примеры планировщиков из README «Расписание
50
+ # обновлений» реально работают -- не зависимости библиотеки,
51
+ # планировщик/очередь задач выбирает пользователь сам.
52
+ "apscheduler>=3.10",
53
+ "celery>=5.3",
54
+ ]
55
+ # Инструменты сборки/публикации -- не нужны для работы или тестов библиотеки.
56
+ release = ["build>=1.0", "twine>=5.0"]
57
+
58
+ [tool.setuptools.packages.find]
59
+ where = ["src"]
60
+
61
+ [tool.setuptools.package-data]
62
+ evesde = ["schema/manifest.json", "py.typed"]
63
+
64
+ [tool.ruff]
65
+ line-length = 100
66
+ target-version = "py310"
67
+ src = ["src"]
68
+
69
+ [tool.ruff.lint]
70
+ select = ["E", "F", "I", "UP", "B", "SIM"]
71
+
72
+ [tool.ruff.lint.per-file-ignores]
73
+ # Фикстуры содержат однострочные SQL-стейтменты (ALTER TABLE ... FOREIGN KEY ...),
74
+ # которые нельзя перенести на несколько строк без изменения семантики теста.
75
+ "**/test_gen_manifest.py" = ["E501"]
76
+ # SQL витрин перенесён дословно из reference/eve_sde_full_schema.sql (см. etl/marts.py) --
77
+ # построчный CASE WHEN пивот на 67 колонок длиннее 100 символов сам по себе.
78
+ "**/marts.py" = ["E501"]
79
+
80
+ [tool.mypy]
81
+ python_version = "3.10"
82
+ strict = true
83
+ files = ["src"]
84
+ mypy_path = "src"
85
+ explicit_package_bases = true
86
+
87
+ [tool.pytest.ini_options]
88
+ testpaths = ["tests"]
89
+ markers = [
90
+ "dbms: интеграционные тесты, требующие реальных MySQL/PostgreSQL (docker-compose)",
91
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,33 @@
1
+ """evesde — библиотека для работы со статическими данными EVE Online (SDE)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from evesde.api.sde import (
6
+ SDE,
7
+ Agent,
8
+ DogmaAttribute,
9
+ IndustryRecipe,
10
+ Item,
11
+ Meta,
12
+ SDEAmbiguousNameError,
13
+ SDENotFoundError,
14
+ SolarSystem,
15
+ TypeStats,
16
+ UpdateResult,
17
+ )
18
+ from evesde.config import SDEConfig
19
+
20
+ __all__ = [
21
+ "SDE",
22
+ "Agent",
23
+ "DogmaAttribute",
24
+ "IndustryRecipe",
25
+ "Item",
26
+ "Meta",
27
+ "SDEAmbiguousNameError",
28
+ "SDENotFoundError",
29
+ "SDEConfig",
30
+ "SolarSystem",
31
+ "TypeStats",
32
+ "UpdateResult",
33
+ ]
@@ -0,0 +1 @@
1
+ """Публичный API запросов evesde."""