winpnp 0.0.2__tar.gz → 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {winpnp-0.0.2 → winpnp-0.1.0}/PKG-INFO +1 -1
  2. {winpnp-0.0.2 → winpnp-0.1.0}/pyproject.toml +1 -1
  3. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/keys/__init__.py +1 -0
  4. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/pnp_property.py +50 -13
  5. winpnp-0.1.0/tests/test_propery_key.py +78 -0
  6. winpnp-0.1.0/tests/test_propery_type.py +68 -0
  7. {winpnp-0.0.2 → winpnp-0.1.0}/.gitignore +0 -0
  8. {winpnp-0.0.2 → winpnp-0.1.0}/.vscode/settings.json +0 -0
  9. {winpnp-0.0.2 → winpnp-0.1.0}/LICENSE +0 -0
  10. {winpnp-0.0.2 → winpnp-0.1.0}/README.md +0 -0
  11. {winpnp-0.0.2 → winpnp-0.1.0}/deploy/build_wheel.bat +0 -0
  12. {winpnp-0.0.2 → winpnp-0.1.0}/deploy/upload_to_pypi.bat +0 -0
  13. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/__init__.py +0 -0
  14. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/_setupapi.py +0 -0
  15. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/info/__init__.py +0 -0
  16. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/info/_pnp_property_mapping.py +0 -0
  17. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/info/device.py +0 -0
  18. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/info/setup_class.py +0 -0
  19. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/__init__.py +0 -0
  20. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/decoding.py +0 -0
  21. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/keys/bluetooth.py +0 -0
  22. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/keys/dev_query.py +0 -0
  23. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/keys/device.py +0 -0
  24. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/keys/device_class.py +0 -0
  25. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/keys/device_container.py +0 -0
  26. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/keys/device_interface.py +0 -0
  27. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/keys/device_interface_class.py +0 -0
  28. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/keys/driver_package.py +0 -0
  29. {winpnp-0.0.2 → winpnp-0.1.0}/src/winpnp/properties/kinds.py +0 -0
  30. {winpnp-0.0.2 → winpnp-0.1.0}/tests/test_property_mappings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: winpnp
3
- Version: 0.0.2
3
+ Version: 0.1.0
4
4
  Summary: A package for interacting with Windows Plug and Play (PnP) entities
5
5
  Project-URL: Homepage, https://github.com/SuperPudding98/winpnp
6
6
  Project-URL: Issues, https://github.com/SuperPudding98/winpnp/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "winpnp"
7
- version = "0.0.2"
7
+ version = "0.1.0"
8
8
  authors = [
9
9
  { name = "Supper Pudding", email = "31290828+SuperPudding98@users.noreply.github.com" },
10
10
  ]
@@ -4,6 +4,7 @@ from winpnp.properties import kinds
4
4
  from winpnp.properties.pnp_property import PnpPropertyKey
5
5
 
6
6
  from . import (
7
+ bluetooth,
7
8
  dev_query,
8
9
  device,
9
10
  device_class,
@@ -15,6 +15,7 @@ class PnpPropertyType(Generic[T]):
15
15
  """
16
16
 
17
17
  _DERIVED_DATA: ClassVar[dict[int, tuple[str, Callable[[bytes], Any]]]] = {}
18
+ _NAME_TO_ID: ClassVar[dict[str, int]] = {}
18
19
 
19
20
  type_id: int
20
21
  name: Optional[str] = field(compare=False)
@@ -48,6 +49,7 @@ class PnpPropertyType(Generic[T]):
48
49
  )
49
50
 
50
51
  PnpPropertyType._DERIVED_DATA[kind.type_id] = (kind.name, kind._decoder)
52
+ PnpPropertyType._NAME_TO_ID[kind.name] = kind.type_id
51
53
 
52
54
  @staticmethod
53
55
  def register_new(
@@ -62,6 +64,14 @@ class PnpPropertyType(Generic[T]):
62
64
  PnpPropertyType.register(kind)
63
65
  return kind
64
66
 
67
+ @staticmethod
68
+ def from_name(name: str) -> Optional["PnpPropertyType[U]"]:
69
+ type_id = PnpPropertyType._NAME_TO_ID.get(name)
70
+ if type_id is None:
71
+ return None
72
+
73
+ return PnpPropertyType(type_id)
74
+
65
75
  def decode(self, data: bytes) -> T:
66
76
  """
67
77
  Decodes raw bytes to the actual python type that this `PnpPropertyType` represents.
@@ -69,13 +79,16 @@ class PnpPropertyType(Generic[T]):
69
79
  return self._decoder(data)
70
80
 
71
81
 
72
- @dataclass(init=False)
82
+ @dataclass(init=False, frozen=True)
73
83
  class PnpPropertyKey(Generic[T]):
74
84
  """
75
85
  A key that specifies a PnP property. This is an abstraction over the Windows DEVPROPKEY struct. Can be used as key for `__getitem__` of various winpnp classes.
76
86
  """
77
87
 
78
- _NAMES: ClassVar[dict[tuple[UUID, int], str]] = {}
88
+ _DERIVED_DATA: ClassVar[
89
+ dict[tuple[UUID, int], tuple[str, Optional[tuple[PnpPropertyType[Any], ...]]]]
90
+ ] = {}
91
+ _NAME_TO_ID: ClassVar[dict[str, tuple[UUID, int]]] = {}
79
92
 
80
93
  category: UUID
81
94
  property_id: int
@@ -89,15 +102,25 @@ class PnpPropertyKey(Generic[T]):
89
102
  name: Optional[str] = None,
90
103
  allowed_types: Optional[Iterable[PnpPropertyType[T]]] = None,
91
104
  ) -> None:
92
- self.category = category
93
- self.property_id = property_id
94
- self.name = (
95
- name if name is not None else self._NAMES.get((category, property_id))
105
+ # Intentionally not setting allowed_types from derived data to allow creating a PnpPropertyKey with registered id but without type limitations
106
+ derived_name, _ = self._DERIVED_DATA.get((category, property_id), (None, None))
107
+
108
+ # Using object.__setattr__ because the class is frozen
109
+ object.__setattr__(self, "category", category)
110
+ object.__setattr__(self, "property_id", property_id)
111
+ object.__setattr__(
112
+ self,
113
+ "name",
114
+ name if name is not None else derived_name,
96
115
  )
97
- self.allowed_types = (
98
- {kind.type_id: kind for kind in allowed_types}
99
- if allowed_types is not None
100
- else None
116
+ object.__setattr__(
117
+ self,
118
+ "allowed_types",
119
+ (
120
+ {kind.type_id: kind for kind in allowed_types}
121
+ if allowed_types is not None
122
+ else None
123
+ ),
101
124
  )
102
125
 
103
126
  @staticmethod
@@ -109,12 +132,17 @@ class PnpPropertyKey(Generic[T]):
109
132
  raise ValueError(f"Cannot register {repr(key)} because it is unnamed.")
110
133
 
111
134
  _id = (key.category, key.property_id)
112
- if _id in PnpPropertyKey._NAMES:
135
+ if _id in PnpPropertyKey._DERIVED_DATA:
113
136
  raise ValueError(
114
- f"Cannot register {repr(key)} because its (category, property_id) pair is already registered with name {repr(PnpPropertyKey._NAMES[_id])}"
137
+ f"Cannot register {repr(key)} because its (category, property_id) pair is already registered with name {repr(PnpPropertyKey._DERIVED_DATA[_id][0])}"
115
138
  )
116
139
 
117
- PnpPropertyKey._NAMES[_id] = key.name
140
+ allowed_types = (
141
+ tuple(key.allowed_types.values()) if key.allowed_types is not None else None
142
+ )
143
+
144
+ PnpPropertyKey._DERIVED_DATA[_id] = (key.name, allowed_types)
145
+ PnpPropertyKey._NAME_TO_ID[key.name] = _id
118
146
 
119
147
  @staticmethod
120
148
  def register_new(
@@ -130,6 +158,15 @@ class PnpPropertyKey(Generic[T]):
130
158
  PnpPropertyKey.register(key)
131
159
  return key
132
160
 
161
+ @staticmethod
162
+ def from_name(name: str) -> Optional["PnpPropertyKey[U]"]:
163
+ _id = PnpPropertyKey._NAME_TO_ID.get(name)
164
+ if _id is None:
165
+ return None
166
+
167
+ _, allowed_types = PnpPropertyKey._DERIVED_DATA.get(_id, (None, None))
168
+ return PnpPropertyKey(*_id, name, allowed_types)
169
+
133
170
 
134
171
  @dataclass()
135
172
  class PnpProperty(Generic[T]):
@@ -0,0 +1,78 @@
1
+ from typing import Any, Iterator, Optional
2
+ from uuid import UUID
3
+
4
+ from pytest_cases import fixture, parametrize_with_cases
5
+
6
+ from winpnp.properties.pnp_property import PnpPropertyKey, PnpPropertyType
7
+
8
+ TEST_KEY_NAME = "test_property_key"
9
+ TEST_KEY_CATEGORY = UUID("{00000000-0000-0000-0000000000000000}")
10
+ TEST_KEY_PROP_ID = 0
11
+ TEST_TYPE_ID = 0xFFFFFFFF
12
+
13
+
14
+ KeyTestParams = tuple[
15
+ Optional[PnpPropertyKey[Any]],
16
+ bool,
17
+ Optional[str],
18
+ Optional[dict[int, PnpPropertyType[Any]]],
19
+ ]
20
+
21
+
22
+ @fixture(scope="function") # type: ignore
23
+ def registered_key() -> Iterator[PnpPropertyKey[None]]:
24
+ key = PnpPropertyKey.register_new(
25
+ TEST_KEY_CATEGORY,
26
+ TEST_KEY_PROP_ID,
27
+ TEST_KEY_NAME,
28
+ (PnpPropertyType(TEST_TYPE_ID),),
29
+ )
30
+ try:
31
+ yield key
32
+ finally:
33
+ del PnpPropertyKey._NAME_TO_ID[TEST_KEY_NAME]
34
+ del PnpPropertyKey._DERIVED_DATA[(TEST_KEY_CATEGORY, TEST_KEY_PROP_ID)]
35
+
36
+
37
+ def case_lookup_not_registered_by_id() -> KeyTestParams:
38
+ return PnpPropertyKey(TEST_KEY_CATEGORY, TEST_KEY_PROP_ID), False, None, None
39
+
40
+
41
+ def case_lookup_not_registered_by_name() -> KeyTestParams:
42
+ return PnpPropertyKey.from_name(TEST_KEY_NAME), True, None, None
43
+
44
+
45
+ def case_lookup_registered_by_id(registered_key: PnpPropertyKey[None]) -> KeyTestParams:
46
+ return (
47
+ PnpPropertyKey(TEST_KEY_CATEGORY, TEST_KEY_PROP_ID),
48
+ False,
49
+ TEST_KEY_NAME,
50
+ None,
51
+ )
52
+
53
+
54
+ def case_lookup_registered_by_name(
55
+ registered_key: PnpPropertyKey[None],
56
+ ) -> KeyTestParams:
57
+ return (
58
+ PnpPropertyKey.from_name(TEST_KEY_NAME),
59
+ False,
60
+ TEST_KEY_NAME,
61
+ {TEST_TYPE_ID: PnpPropertyType(TEST_TYPE_ID)},
62
+ )
63
+
64
+
65
+ @parametrize_with_cases(
66
+ ("key", "should_be_none", "expected_name", "expected_allowed_types"), cases="."
67
+ )
68
+ def test_registration(
69
+ key: Optional[PnpPropertyKey[Any]],
70
+ should_be_none: bool,
71
+ expected_name: Optional[str],
72
+ expected_allowed_types: Optional[dict[int, PnpPropertyType[Any]]],
73
+ ) -> None:
74
+ assert (key is None) == should_be_none
75
+
76
+ if key is not None:
77
+ assert key.name == expected_name
78
+ assert key.allowed_types == expected_allowed_types
@@ -0,0 +1,68 @@
1
+ from typing import Any, Callable, Iterator, Optional
2
+
3
+ from pytest_cases import fixture, parametrize_with_cases
4
+
5
+ from winpnp.properties.decoding import decode_raw
6
+ from winpnp.properties.pnp_property import PnpPropertyType
7
+
8
+ TEST_TYPE_NAME = "test_property_type"
9
+ TEST_TYPE_ID = 0xFFFFFFFF
10
+
11
+ TypeTestParams = tuple[
12
+ Optional[PnpPropertyType[Any]], bool, Optional[str], Callable[[bytes], Any]
13
+ ]
14
+
15
+
16
+ def _test_decoder(data: bytes) -> None:
17
+ return None
18
+
19
+
20
+ @fixture(scope="function") # type: ignore
21
+ def registered_type() -> Iterator[PnpPropertyType[None]]:
22
+ kind = PnpPropertyType.register_new(TEST_TYPE_ID, TEST_TYPE_NAME, _test_decoder)
23
+ try:
24
+ yield kind
25
+ finally:
26
+ del PnpPropertyType._NAME_TO_ID[TEST_TYPE_NAME]
27
+ del PnpPropertyType._DERIVED_DATA[TEST_TYPE_ID]
28
+
29
+
30
+ def case_lookup_not_registered_by_id() -> TypeTestParams:
31
+ return PnpPropertyType(TEST_TYPE_ID), False, None, decode_raw
32
+
33
+
34
+ def case_lookup_not_registered_by_name() -> TypeTestParams:
35
+ return PnpPropertyType.from_name(TEST_TYPE_NAME), True, None, decode_raw
36
+
37
+
38
+ def case_lookup_registered_by_id(
39
+ registered_type: PnpPropertyType[None],
40
+ ) -> TypeTestParams:
41
+ return PnpPropertyType(TEST_TYPE_ID), False, TEST_TYPE_NAME, _test_decoder
42
+
43
+
44
+ def case_lookup_registered_by_name(
45
+ registered_type: PnpPropertyType[None],
46
+ ) -> TypeTestParams:
47
+ return (
48
+ PnpPropertyType.from_name(TEST_TYPE_NAME),
49
+ False,
50
+ TEST_TYPE_NAME,
51
+ _test_decoder,
52
+ )
53
+
54
+
55
+ @parametrize_with_cases(
56
+ ("kind", "should_be_none", "expected_name", "expected_decoder"), cases="."
57
+ )
58
+ def test_registration(
59
+ kind: Optional[PnpPropertyType[Any]],
60
+ should_be_none: bool,
61
+ expected_name: Optional[str],
62
+ expected_decoder: Callable[[bytes], Any],
63
+ ) -> None:
64
+ assert (kind is None) == should_be_none
65
+
66
+ if kind is not None:
67
+ assert kind.name == expected_name
68
+ assert kind._decoder is expected_decoder
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes