python-ember-mug 1.2.0b1__tar.gz → 1.3.0b1__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.
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/PKG-INFO +7 -5
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/README.md +3 -0
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/ember_mug/__init__.py +1 -1
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/ember_mug/cli/commands.py +10 -5
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/ember_mug/cli/helpers.py +2 -2
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/ember_mug/data.py +1 -1
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/ember_mug/mug.py +25 -8
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/ember_mug/utils.py +8 -10
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/pyproject.toml +36 -42
- python_ember_mug-1.2.0b1/tests/__init__.py +0 -1
- python_ember_mug-1.2.0b1/tests/cli/__init__.py +0 -1
- python_ember_mug-1.2.0b1/tests/cli/test_commands.py +0 -380
- python_ember_mug-1.2.0b1/tests/cli/test_helpers.py +0 -92
- python_ember_mug-1.2.0b1/tests/conftest.py +0 -99
- python_ember_mug-1.2.0b1/tests/test_connection.py +0 -584
- python_ember_mug-1.2.0b1/tests/test_consts.py +0 -41
- python_ember_mug-1.2.0b1/tests/test_data.py +0 -115
- python_ember_mug-1.2.0b1/tests/test_formatting.py +0 -24
- python_ember_mug-1.2.0b1/tests/test_mug_data.py +0 -120
- python_ember_mug-1.2.0b1/tests/test_scanner.py +0 -71
- python_ember_mug-1.2.0b1/tests/test_utils.py +0 -224
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/.gitignore +0 -0
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/LICENSE +0 -0
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/ember_mug/__main__.py +0 -0
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/ember_mug/cli/__init__.py +0 -0
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/ember_mug/consts.py +0 -0
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/ember_mug/formatting.py +0 -0
- {python_ember_mug-1.2.0b1 → python_ember_mug-1.3.0b1}/ember_mug/scanner.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-ember-mug
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0b1
|
|
4
4
|
Summary: Python Library for Ember Mugs.
|
|
5
5
|
Project-URL: Changelog, https://sopelj.github.io/python-ember-mug/changelog/
|
|
6
6
|
Project-URL: Documentation, https://sopelj.github.io/python-ember-mug/
|
|
7
7
|
Project-URL: Source code, https://github.com/sopelj/python-ember-mug/
|
|
8
8
|
Project-URL: Bug Tracker, https://github.com/sopelj/python-ember-mug/issues
|
|
9
|
-
Author-email: Jesse Sopel <jesse
|
|
9
|
+
Author-email: Jesse Sopel <jesse@sopelj.ca>
|
|
10
10
|
License-Expression: MIT
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
@@ -17,9 +17,8 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Requires-Python: >=3.11
|
|
20
|
-
Requires-Dist: bleak-retry-connector>=
|
|
21
|
-
Requires-Dist: bleak>=0.
|
|
22
|
-
Requires-Dist: bleak>=0.22.3; python_version <= '3.13'
|
|
20
|
+
Requires-Dist: bleak-retry-connector>=4.0.2
|
|
21
|
+
Requires-Dist: bleak>=1.0.1
|
|
23
22
|
Provides-Extra: dev
|
|
24
23
|
Requires-Dist: ipython; extra == 'dev'
|
|
25
24
|
Provides-Extra: docs
|
|
@@ -207,6 +206,9 @@ You may also need to re-add it to the app in order to make it writable again as
|
|
|
207
206
|
This seems to be caused by the bluetooth adaptor being in some sort of passive mode. I have not yet figured out how to wake it programmatically so sadly, you need to manually open `bluetoothctl` to do so.
|
|
208
207
|
Please ensure the device is in pairing mode (ie the light is flashing blue or says "PAIR") and run the `bluetoothctl` command. You don't need to type anything. run it and wait until the mug connects.
|
|
209
208
|
|
|
209
|
+
### Model incorrect or not found
|
|
210
|
+
|
|
211
|
+
I don't have a lot of these devices, so if this library does not correctly identify your device, please open and issue with the advertisement data of your device, so I can update the library to correctly identify it. Thanks!
|
|
210
212
|
|
|
211
213
|
## Development
|
|
212
214
|
|
|
@@ -166,6 +166,9 @@ You may also need to re-add it to the app in order to make it writable again as
|
|
|
166
166
|
This seems to be caused by the bluetooth adaptor being in some sort of passive mode. I have not yet figured out how to wake it programmatically so sadly, you need to manually open `bluetoothctl` to do so.
|
|
167
167
|
Please ensure the device is in pairing mode (ie the light is flashing blue or says "PAIR") and run the `bluetoothctl` command. You don't need to type anything. run it and wait until the mug connects.
|
|
168
168
|
|
|
169
|
+
### Model incorrect or not found
|
|
170
|
+
|
|
171
|
+
I don't have a lot of these devices, so if this library does not correctly identify your device, please open and issue with the advertisement data of your device, so I can update the library to correctly identify it. Thanks!
|
|
169
172
|
|
|
170
173
|
## Development
|
|
171
174
|
|
|
@@ -12,8 +12,8 @@ from typing import TYPE_CHECKING, ClassVar
|
|
|
12
12
|
|
|
13
13
|
from bleak import AdvertisementData, BleakError
|
|
14
14
|
|
|
15
|
-
from ember_mug.consts import ATTR_LABELS, EXTRA_ATTRS, IS_LINUX, VolumeLevel
|
|
16
|
-
from ember_mug.data import Colour
|
|
15
|
+
from ember_mug.consts import ATTR_LABELS, EMBER_BLE_SIG, EXTRA_ATTRS, IS_LINUX, VolumeLevel
|
|
16
|
+
from ember_mug.data import Colour, DeviceModel
|
|
17
17
|
from ember_mug.mug import EmberMug
|
|
18
18
|
from ember_mug.scanner import discover_devices, find_device
|
|
19
19
|
|
|
@@ -33,9 +33,14 @@ get_attribute_names = [n.replace("_", "-") for n in all_attrs]
|
|
|
33
33
|
async def get_device(args: Namespace) -> EmberMug:
|
|
34
34
|
"""Help to get the devices based on command args."""
|
|
35
35
|
device, advertisement = await find_device_cmd(args)
|
|
36
|
+
model_info = get_model_info_from_advertiser_data(advertisement)
|
|
37
|
+
if model_info.model == DeviceModel.UNKNOWN_DEVICE and not args.raw:
|
|
38
|
+
data = advertisement.manufacturer_data.get(EMBER_BLE_SIG, None)
|
|
39
|
+
print(f"Warning: No model found matching advertisement data: {data!r}")
|
|
40
|
+
|
|
36
41
|
mug = EmberMug(
|
|
37
42
|
device,
|
|
38
|
-
|
|
43
|
+
model_info,
|
|
39
44
|
use_metric=not args.imperial,
|
|
40
45
|
debug=args.debug,
|
|
41
46
|
)
|
|
@@ -139,13 +144,13 @@ async def set_device_value_cmd(args: Namespace) -> None:
|
|
|
139
144
|
if not values:
|
|
140
145
|
print("Please specify at least one attribute and value to set.")
|
|
141
146
|
options = [f"--{a.replace('_', '-')}" for a in attrs]
|
|
142
|
-
print(f
|
|
147
|
+
print(f"Options: {', '.join(options)}")
|
|
143
148
|
sys.exit(1)
|
|
144
149
|
|
|
145
150
|
mug = await get_device(args)
|
|
146
151
|
async with mug.connection(adapter=args.adapter):
|
|
147
152
|
for attr, value in values:
|
|
148
|
-
method = getattr(mug, f
|
|
153
|
+
method = getattr(mug, f"set_{attr.replace('-', '_')}")
|
|
149
154
|
print(f"Setting {attr} to {value}")
|
|
150
155
|
try:
|
|
151
156
|
await method(value)
|
|
@@ -47,11 +47,11 @@ def print_table(data: list[tuple[str, ...]]) -> None:
|
|
|
47
47
|
rows = [build_sub_rows(r) for r in data]
|
|
48
48
|
num_columns = max(len(sr) for r in rows for sr in r.values())
|
|
49
49
|
column_sizes = [max(len(sr[i]) for r in rows for sr in r.values()) + 2 for i in range(num_columns)]
|
|
50
|
-
vertical = f
|
|
50
|
+
vertical = f"+{'+'.join('-' * i for i in column_sizes)}+"
|
|
51
51
|
print(vertical)
|
|
52
52
|
for row in rows:
|
|
53
53
|
for sub_row in row.values():
|
|
54
|
-
inner = "|".join(f" {sub_row[i]:<{width-2}} " for i, width in enumerate(column_sizes))
|
|
54
|
+
inner = "|".join(f" {sub_row[i]:<{width - 2}} " for i, width in enumerate(column_sizes))
|
|
55
55
|
print(f"|{inner}|")
|
|
56
56
|
print(vertical)
|
|
57
57
|
|
|
@@ -87,7 +87,7 @@ class BatteryInfo(AsDict):
|
|
|
87
87
|
|
|
88
88
|
def __str__(self) -> str:
|
|
89
89
|
"""Format nicely for printing."""
|
|
90
|
-
return f
|
|
90
|
+
return f"{self.percent}%, {'' if self.on_charging_base else 'not '}on charging base"
|
|
91
91
|
|
|
92
92
|
|
|
93
93
|
@dataclass
|
|
@@ -31,6 +31,7 @@ from .utils import (
|
|
|
31
31
|
bytes_to_big_int,
|
|
32
32
|
bytes_to_little_int,
|
|
33
33
|
convert_temp_to_celsius,
|
|
34
|
+
convert_temp_to_fahrenheit,
|
|
34
35
|
decode_byte_string,
|
|
35
36
|
discover_services,
|
|
36
37
|
encode_byte_string,
|
|
@@ -44,6 +45,8 @@ if TYPE_CHECKING:
|
|
|
44
45
|
from bleak.backends.characteristic import BleakGATTCharacteristic
|
|
45
46
|
from bleak.backends.device import BLEDevice
|
|
46
47
|
|
|
48
|
+
TempUnitType = Literal["°C", "°F"] | TemperatureUnit | Enum
|
|
49
|
+
|
|
47
50
|
|
|
48
51
|
logger = logging.getLogger(__name__)
|
|
49
52
|
|
|
@@ -130,6 +133,22 @@ class EmberMug:
|
|
|
130
133
|
"""Check if the mug can support write operations."""
|
|
131
134
|
return self.data.udsk is not None
|
|
132
135
|
|
|
136
|
+
def _convert_to_device_unit(self, value: float) -> float:
|
|
137
|
+
"""Convert user value to the unit the device expects."""
|
|
138
|
+
if self.data.use_metric and self.data.temperature_unit != TemperatureUnit.CELSIUS:
|
|
139
|
+
return convert_temp_to_fahrenheit(value)
|
|
140
|
+
if not self.data.use_metric and self.data.temperature_unit != TemperatureUnit.FAHRENHEIT:
|
|
141
|
+
return convert_temp_to_celsius(value)
|
|
142
|
+
return value
|
|
143
|
+
|
|
144
|
+
def _convert_to_user_unit(self, value: float) -> float:
|
|
145
|
+
"""Convert device value to the unit the user expects."""
|
|
146
|
+
if self.data.use_metric and self.data.temperature_unit != TemperatureUnit.CELSIUS:
|
|
147
|
+
return convert_temp_to_celsius(value)
|
|
148
|
+
if not self.data.use_metric and self.data.temperature_unit != TemperatureUnit.FAHRENHEIT:
|
|
149
|
+
return convert_temp_to_fahrenheit(value)
|
|
150
|
+
return value
|
|
151
|
+
|
|
133
152
|
def has_attribute(self, attribute: str) -> bool:
|
|
134
153
|
"""Check whether the device has the given attribute."""
|
|
135
154
|
return attribute in self.data.model_info.device_attributes
|
|
@@ -155,7 +174,7 @@ class EmberMug:
|
|
|
155
174
|
disconnected_callback=self._disconnect_callback,
|
|
156
175
|
ble_device_callback=lambda: self.device,
|
|
157
176
|
)
|
|
158
|
-
if self.debug
|
|
177
|
+
if self.debug:
|
|
159
178
|
await discover_services(client)
|
|
160
179
|
self._expected_disconnect = False
|
|
161
180
|
except (TimeoutError, BleakError) as error:
|
|
@@ -273,7 +292,7 @@ class EmberMug:
|
|
|
273
292
|
async def get_target_temp(self) -> float:
|
|
274
293
|
"""Get target temp form mug gatt."""
|
|
275
294
|
temp_bytes = await self._read(MugCharacteristic.TARGET_TEMPERATURE)
|
|
276
|
-
return temp_from_bytes(temp_bytes
|
|
295
|
+
return self._convert_to_user_unit(temp_from_bytes(temp_bytes))
|
|
277
296
|
|
|
278
297
|
async def set_target_temp(self, target_temp: float) -> None:
|
|
279
298
|
"""Set new target temp for mug."""
|
|
@@ -282,17 +301,15 @@ class EmberMug:
|
|
|
282
301
|
if target_temp != 0 and not (min_temp <= target_temp <= max_temp):
|
|
283
302
|
raise ValueError(f"Temperature should be between {min_temp} and {max_temp} or 0.")
|
|
284
303
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
target = bytearray(int(target_temp / 0.01).to_bytes(2, "little"))
|
|
304
|
+
target_temp = self._convert_to_device_unit(target_temp)
|
|
305
|
+
target = bytearray(round(target_temp / 0.01).to_bytes(2, "little"))
|
|
289
306
|
await self._write(MugCharacteristic.TARGET_TEMPERATURE, target)
|
|
290
307
|
self.data.target_temp = target_temp
|
|
291
308
|
|
|
292
309
|
async def get_current_temp(self) -> float:
|
|
293
310
|
"""Get current temp from mug gatt."""
|
|
294
311
|
temp_bytes = await self._read(MugCharacteristic.CURRENT_TEMPERATURE)
|
|
295
|
-
return temp_from_bytes(temp_bytes
|
|
312
|
+
return self._convert_to_user_unit(temp_from_bytes(temp_bytes))
|
|
296
313
|
|
|
297
314
|
async def get_liquid_level(self) -> int:
|
|
298
315
|
"""Get liquid level from mug gatt."""
|
|
@@ -368,7 +385,7 @@ class EmberMug:
|
|
|
368
385
|
return TemperatureUnit.CELSIUS
|
|
369
386
|
return TemperatureUnit.FAHRENHEIT
|
|
370
387
|
|
|
371
|
-
async def set_temperature_unit(self, unit:
|
|
388
|
+
async def set_temperature_unit(self, unit: TempUnitType) -> None:
|
|
372
389
|
"""Set mug unit."""
|
|
373
390
|
text_unit = unit.value if isinstance(unit, Enum) else unit
|
|
374
391
|
unit_bytes = bytearray([1 if text_unit == TemperatureUnit.FAHRENHEIT else 0])
|
|
@@ -56,15 +56,12 @@ def convert_temp_to_celsius(temp: float) -> float:
|
|
|
56
56
|
return (temp - 32) * 5 / 9
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
def temp_from_bytes(temp_bytes: bytearray
|
|
60
|
-
"""Get temperature from bytearray
|
|
61
|
-
|
|
62
|
-
if metric is False:
|
|
63
|
-
temp = convert_temp_to_fahrenheit(temp)
|
|
64
|
-
return round(temp, 2)
|
|
59
|
+
def temp_from_bytes(temp_bytes: bytearray) -> float:
|
|
60
|
+
"""Get temperature from bytearray."""
|
|
61
|
+
return float(bytes_to_little_int(temp_bytes)) * 0.01
|
|
65
62
|
|
|
66
63
|
|
|
67
|
-
def get_colour_from_int(colour_id: int) -> DeviceColour | None:
|
|
64
|
+
def get_colour_from_int(colour_id: int) -> DeviceColour | None: # noqa: PLR0911
|
|
68
65
|
"""Extrapolate device colour from integer in advertiser data."""
|
|
69
66
|
if colour_id in (-127, -63, 1, 14, 65):
|
|
70
67
|
return DeviceColour.BLACK
|
|
@@ -76,13 +73,14 @@ def get_colour_from_int(colour_id: int) -> DeviceColour | None:
|
|
|
76
73
|
return DeviceColour.COPPER
|
|
77
74
|
if colour_id in (-124, -60):
|
|
78
75
|
return DeviceColour.ROSE_GOLD
|
|
76
|
+
if colour_id in (-59, -123):
|
|
77
|
+
return DeviceColour.STAINLESS_STEEL
|
|
79
78
|
return {
|
|
80
79
|
-51: DeviceColour.SANDSTONE,
|
|
81
80
|
-52: DeviceColour.SAGE_GREEN,
|
|
82
81
|
-55: DeviceColour.GREY,
|
|
83
82
|
-57: DeviceColour.BLUE,
|
|
84
83
|
-122: DeviceColour.GOLD,
|
|
85
|
-
-123: DeviceColour.STAINLESS_STEEL,
|
|
86
84
|
}.get(colour_id)
|
|
87
85
|
|
|
88
86
|
|
|
@@ -97,7 +95,7 @@ def get_model_from_single_int_and_services( # noqa PLR0911
|
|
|
97
95
|
return DeviceModel.MUG_1_10_OZ
|
|
98
96
|
if model_id == 65:
|
|
99
97
|
return DeviceModel.MUG_1_14_OZ
|
|
100
|
-
if model_id in (-51, -63, -61, -62):
|
|
98
|
+
if model_id in (-51, -59, -63, -61, -62):
|
|
101
99
|
return DeviceModel.MUG_2_14_OZ
|
|
102
100
|
if model_id == -60:
|
|
103
101
|
return DeviceModel.CUP_6_OZ
|
|
@@ -150,7 +148,7 @@ def get_model_info_from_advertiser_data(advertisement: AdvertisementData) -> Mod
|
|
|
150
148
|
get_colour_from_int(colour_id),
|
|
151
149
|
)
|
|
152
150
|
logger.debug(
|
|
153
|
-
"Unable to reliably determine model info from advertiser data.
|
|
151
|
+
"Unable to reliably determine model info from advertiser data.Falling back to guessing based on name.",
|
|
154
152
|
)
|
|
155
153
|
return ModelInfo(guess_model_from_name(advertisement.local_name))
|
|
156
154
|
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
name = "python-ember-mug"
|
|
3
3
|
readme = "README.md"
|
|
4
4
|
description = "Python Library for Ember Mugs."
|
|
5
|
-
authors = [
|
|
6
|
-
|
|
7
|
-
]
|
|
8
|
-
license = "MIT"
|
|
5
|
+
authors = [{ name = "Jesse Sopel", email = "jesse@sopelj.ca" }]
|
|
6
|
+
license = "MIT"
|
|
9
7
|
requires-python = ">=3.11"
|
|
10
8
|
dynamic = ["version"]
|
|
11
|
-
classifiers=[
|
|
9
|
+
classifiers = [
|
|
12
10
|
'Intended Audience :: Developers',
|
|
13
11
|
'License :: OSI Approved :: MIT License',
|
|
14
12
|
'Natural Language :: English',
|
|
@@ -18,32 +16,25 @@ classifiers=[
|
|
|
18
16
|
'Programming Language :: Python :: 3.13',
|
|
19
17
|
]
|
|
20
18
|
dependencies = [
|
|
21
|
-
"bleak-retry-connector>=
|
|
22
|
-
"bleak>=0.
|
|
23
|
-
"bleak>=0.22.3; python_version <= '3.13'",
|
|
19
|
+
"bleak-retry-connector>=4.0.2",
|
|
20
|
+
"bleak>=1.0.1",
|
|
24
21
|
]
|
|
25
22
|
|
|
26
23
|
[project.optional-dependencies]
|
|
27
|
-
test = [
|
|
28
|
-
"pytest>=7.2.1",
|
|
29
|
-
"pytest-cov",
|
|
30
|
-
"pytest-asyncio",
|
|
31
|
-
]
|
|
24
|
+
test = ["pytest>=7.2.1", "pytest-cov", "pytest-asyncio"]
|
|
32
25
|
docs = [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
]
|
|
44
|
-
dev = [
|
|
45
|
-
"ipython",
|
|
26
|
+
"mkdocs>=1.6.1",
|
|
27
|
+
"mkdocs-include-markdown-plugin>=7.0.0,<8.0.0",
|
|
28
|
+
"mkdocs-material>=9.5.44,<10.0.0",
|
|
29
|
+
"mkdocs-material-extensions",
|
|
30
|
+
"mkdocstrings-python>=1.12.0",
|
|
31
|
+
"mkdocs-autorefs",
|
|
32
|
+
"mkdocs-literate-nav",
|
|
33
|
+
"mkdocs-gen-files",
|
|
34
|
+
"black",
|
|
35
|
+
"termynal",
|
|
46
36
|
]
|
|
37
|
+
dev = ["ipython"]
|
|
47
38
|
|
|
48
39
|
[project.urls]
|
|
49
40
|
"Changelog" = "https://sopelj.github.io/python-ember-mug/changelog/"
|
|
@@ -63,16 +54,19 @@ build-backend = "hatchling.build"
|
|
|
63
54
|
[tool.hatch.version]
|
|
64
55
|
path = "ember_mug/__init__.py"
|
|
65
56
|
|
|
57
|
+
[tool.hatch.build]
|
|
58
|
+
exclude = ["/docs", "/tests"]
|
|
59
|
+
|
|
66
60
|
[tool.hatch.build.targets.sdist]
|
|
67
|
-
packages = ["ember_mug"
|
|
61
|
+
packages = ["ember_mug"]
|
|
68
62
|
exclude = [".gitignore"]
|
|
69
63
|
|
|
70
64
|
[tool.hatch.build.targets.wheel]
|
|
71
|
-
packages = ["ember_mug"
|
|
65
|
+
packages = ["ember_mug"]
|
|
72
66
|
exclude = [".gitignore"]
|
|
73
67
|
|
|
74
68
|
[tool.hatch.envs.default]
|
|
75
|
-
python = "3.
|
|
69
|
+
python = "3.13"
|
|
76
70
|
|
|
77
71
|
[tool.hatch.envs.test]
|
|
78
72
|
features = ["test"]
|
|
@@ -85,7 +79,7 @@ cov = "pytest -vvv --asyncio-mode=auto --cov=ember_mug --cov-branch --cov-report
|
|
|
85
79
|
no-cov = "cov --no-cov"
|
|
86
80
|
|
|
87
81
|
[tool.hatch.envs.docs]
|
|
88
|
-
python = "3.
|
|
82
|
+
python = "3.13"
|
|
89
83
|
features = ["docs"]
|
|
90
84
|
|
|
91
85
|
[tool.hatch.envs.docs.scripts]
|
|
@@ -95,7 +89,7 @@ serve = "mkdocs serve --dev-addr localhost:8000"
|
|
|
95
89
|
[tool.black]
|
|
96
90
|
line-length = 120
|
|
97
91
|
skip-string-normalization = true
|
|
98
|
-
target-version = ["py311", "py312"]
|
|
92
|
+
target-version = ["py311", "py312", "py313"]
|
|
99
93
|
include = '\.pyi?$'
|
|
100
94
|
exclude = '''
|
|
101
95
|
/(
|
|
@@ -112,15 +106,15 @@ exclude = '''
|
|
|
112
106
|
|
|
113
107
|
[tool.coverage.report]
|
|
114
108
|
exclude_lines = [
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
109
|
+
"pragma: no cover",
|
|
110
|
+
"def __repr__",
|
|
111
|
+
"def __str__",
|
|
112
|
+
"def main",
|
|
113
|
+
"raise AssertionError",
|
|
114
|
+
"raise NotImplementedError",
|
|
115
|
+
"if __name__ == .__main__.:",
|
|
116
|
+
"if TYPE_CHECKING:",
|
|
117
|
+
"if typing.TYPE_CHECKING:",
|
|
124
118
|
]
|
|
125
119
|
|
|
126
120
|
[tool.ruff]
|
|
@@ -163,7 +157,7 @@ ignore = ["D203", "D212", "PLR2004"]
|
|
|
163
157
|
|
|
164
158
|
[tool.ruff.lint.per-file-ignores]
|
|
165
159
|
"tests/**/*.py" = [
|
|
166
|
-
"D103",
|
|
167
|
-
"S101",
|
|
160
|
+
"D103", # No docstrings in tests needed
|
|
161
|
+
"S101", # We needs asserts in tests
|
|
168
162
|
"SLF001",
|
|
169
163
|
]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Unit test package for python-ember-mug."""
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Tests for the CLI module."""
|