nwp500-python 6.0.1__tar.gz → 6.0.2__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 (136) hide show
  1. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/CHANGELOG.rst +15 -0
  2. {nwp500_python-6.0.1/src/nwp500_python.egg-info → nwp500_python-6.0.2}/PKG-INFO +2 -2
  3. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/scripts/bump_version.py +1 -24
  4. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/setup.cfg +1 -1
  5. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/auth.py +20 -3
  6. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/models.py +6 -0
  7. {nwp500_python-6.0.1 → nwp500_python-6.0.2/src/nwp500_python.egg-info}/PKG-INFO +2 -2
  8. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500_python.egg-info/requires.txt +1 -1
  9. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/.coveragerc +0 -0
  10. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/.github/copilot-instructions.md +0 -0
  11. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/.github/workflows/ci.yml +0 -0
  12. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/.github/workflows/release.yml +0 -0
  13. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/.gitignore +0 -0
  14. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/.pre-commit-config.yaml +0 -0
  15. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/.readthedocs.yml +0 -0
  16. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/AUTHORS.rst +0 -0
  17. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/CONTRIBUTING.rst +0 -0
  18. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/LICENSE.txt +0 -0
  19. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/Makefile +0 -0
  20. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/README.rst +0 -0
  21. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/RELEASE.md +0 -0
  22. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/Makefile +0 -0
  23. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/_static/.gitignore +0 -0
  24. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/authors.rst +0 -0
  25. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/changelog.rst +0 -0
  26. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/conf.py +0 -0
  27. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/configuration.rst +0 -0
  28. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/development/contributing.rst +0 -0
  29. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/development/history.rst +0 -0
  30. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/guides/advanced_features_explained.rst +0 -0
  31. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/guides/auto_recovery.rst +0 -0
  32. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/guides/command_queue.rst +0 -0
  33. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/guides/energy_monitoring.rst +0 -0
  34. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/guides/event_system.rst +0 -0
  35. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/guides/reservations.rst +0 -0
  36. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/guides/scheduling_features.rst +0 -0
  37. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/guides/time_of_use.rst +0 -0
  38. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/index.rst +0 -0
  39. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/installation.rst +0 -0
  40. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/license.rst +0 -0
  41. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/openapi.yaml +0 -0
  42. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/protocol/data_conversions.rst +0 -0
  43. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/protocol/device_features.rst +0 -0
  44. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/protocol/device_status.rst +0 -0
  45. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/protocol/error_codes.rst +0 -0
  46. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/protocol/firmware_tracking.rst +0 -0
  47. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/protocol/mqtt_protocol.rst +0 -0
  48. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/protocol/rest_api.rst +0 -0
  49. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/python_api/api_client.rst +0 -0
  50. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/python_api/auth_client.rst +0 -0
  51. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/python_api/cli.rst +0 -0
  52. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/python_api/constants.rst +0 -0
  53. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/python_api/events.rst +0 -0
  54. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/python_api/exceptions.rst +0 -0
  55. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/python_api/models.rst +0 -0
  56. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/python_api/mqtt_client.rst +0 -0
  57. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/quickstart.rst +0 -0
  58. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/docs/requirements.txt +0 -0
  59. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/.ruff.toml +0 -0
  60. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/README.md +0 -0
  61. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/anti_legionella_example.py +0 -0
  62. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/api_client_example.py +0 -0
  63. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/auth_constructor_example.py +0 -0
  64. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/authenticate.py +0 -0
  65. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/auto_recovery_example.py +0 -0
  66. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/combined_callbacks.py +0 -0
  67. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/command_queue_demo.py +0 -0
  68. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/device_feature_callback.py +0 -0
  69. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/device_status_callback.py +0 -0
  70. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/device_status_callback_debug.py +0 -0
  71. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/energy_usage_example.py +0 -0
  72. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/event_emitter_demo.py +0 -0
  73. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/exception_handling_example.py +0 -0
  74. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/improved_auth_pattern.py +0 -0
  75. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/mask.py +0 -0
  76. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/mqtt_client_example.py +0 -0
  77. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/periodic_device_info.py +0 -0
  78. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/periodic_requests.py +0 -0
  79. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/power_control_example.py +0 -0
  80. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/reconnection_demo.py +0 -0
  81. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/reservation_schedule_example.py +0 -0
  82. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/set_dhw_temperature_example.py +0 -0
  83. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/set_mode_example.py +0 -0
  84. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/simple_auto_recovery.py +0 -0
  85. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/simple_periodic_info.py +0 -0
  86. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/simple_periodic_status.py +0 -0
  87. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/test_api_client.py +0 -0
  88. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/test_mqtt_connection.py +0 -0
  89. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/test_mqtt_messaging.py +0 -0
  90. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/test_periodic_minimal.py +0 -0
  91. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/token_restoration_example.py +0 -0
  92. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/tou_openei_example.py +0 -0
  93. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/examples/tou_schedule_example.py +0 -0
  94. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/pyproject.toml +0 -0
  95. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/scripts/README.md +0 -0
  96. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/scripts/format.py +0 -0
  97. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/scripts/lint.py +0 -0
  98. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/scripts/setup-dev.py +0 -0
  99. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/scripts/validate_version.py +0 -0
  100. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/setup.py +0 -0
  101. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/__init__.py +0 -0
  102. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/api_client.py +0 -0
  103. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/cli/__init__.py +0 -0
  104. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/cli/__main__.py +0 -0
  105. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/cli/commands.py +0 -0
  106. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/cli/monitoring.py +0 -0
  107. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/cli/output_formatters.py +0 -0
  108. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/cli/token_storage.py +0 -0
  109. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/config.py +0 -0
  110. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/constants.py +0 -0
  111. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/encoding.py +0 -0
  112. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/events.py +0 -0
  113. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/exceptions.py +0 -0
  114. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/mqtt_client.py +0 -0
  115. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/mqtt_command_queue.py +0 -0
  116. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/mqtt_connection.py +0 -0
  117. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/mqtt_device_control.py +0 -0
  118. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/mqtt_periodic.py +0 -0
  119. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/mqtt_reconnection.py +0 -0
  120. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/mqtt_subscriptions.py +0 -0
  121. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/mqtt_utils.py +0 -0
  122. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/py.typed +0 -0
  123. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500/utils.py +0 -0
  124. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500_python.egg-info/SOURCES.txt +0 -0
  125. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
  126. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500_python.egg-info/entry_points.txt +0 -0
  127. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500_python.egg-info/not-zip-safe +0 -0
  128. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/src/nwp500_python.egg-info/top_level.txt +0 -0
  129. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/tests/conftest.py +0 -0
  130. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/tests/test_api_helpers.py +0 -0
  131. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/tests/test_auth.py +0 -0
  132. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/tests/test_command_queue.py +0 -0
  133. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/tests/test_events.py +0 -0
  134. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/tests/test_exceptions.py +0 -0
  135. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/tests/test_utils.py +0 -0
  136. {nwp500_python-6.0.1 → nwp500_python-6.0.2}/tox.ini +0 -0
@@ -2,6 +2,21 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version 6.0.2 (2025-11-15)
6
+ ==========================
7
+
8
+ Fixed
9
+ -----
10
+
11
+ - DNS resolution in containerized environments using ThreadedResolver
12
+ - Updated AWS IoT library version
13
+ - Device status field conversions
14
+
15
+ Changed
16
+ -------
17
+
18
+ - Refactored ThreadedResolver session creation into helper method
19
+
5
20
  Version 6.0.1 (2025-11-06)
6
21
  ==========================
7
22
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 6.0.1
3
+ Version: 6.0.2
4
4
  Summary: A library for controlling Navien NWP500 Water Heaters via NaviLink
5
5
  Home-page: https://github.com/eman/nwp500-python
6
6
  Author: Emmanuel Levijarvi
@@ -23,7 +23,7 @@ Requires-Python: >=3.9
23
23
  Description-Content-Type: text/x-rst; charset=UTF-8
24
24
  License-File: LICENSE.txt
25
25
  Requires-Dist: aiohttp>=3.8.0
26
- Requires-Dist: awsiotsdk>=1.25.0
26
+ Requires-Dist: awsiotsdk>=1.26.0
27
27
  Provides-Extra: testing
28
28
  Requires-Dist: setuptools; extra == "testing"
29
29
  Requires-Dist: pytest; extra == "testing"
@@ -107,26 +107,14 @@ def validate_version_progression(current: str, new: str) -> None:
107
107
  if major_jump > 1:
108
108
  print(f"Warning: Large major version jump detected ({current} -> {new})")
109
109
  print(f"This will jump from {curr_major}.x.x to {new_major}.x.x")
110
- response = input("Are you sure? (yes/no): ")
111
- if response.lower() != "yes":
112
- print("Version bump cancelled.")
113
- sys.exit(1)
114
110
 
115
111
  if major_jump == 0 and minor_jump > 5:
116
112
  print(f"Warning: Large minor version jump detected ({current} -> {new})")
117
113
  print(f"This will jump from x.{curr_minor}.x to x.{new_minor}.x")
118
- response = input("Are you sure? (yes/no): ")
119
- if response.lower() != "yes":
120
- print("Version bump cancelled.")
121
- sys.exit(1)
122
114
 
123
115
  if major_jump == 0 and minor_jump == 0 and patch_jump > 10:
124
116
  print(f"Warning: Large patch version jump detected ({current} -> {new})")
125
117
  print(f"This will jump from x.x.{curr_patch} to x.x.{new_patch}")
126
- response = input("Are you sure? (yes/no): ")
127
- if response.lower() != "yes":
128
- print("Version bump cancelled.")
129
- sys.exit(1)
130
118
 
131
119
 
132
120
  def check_working_directory_clean() -> None:
@@ -201,19 +189,8 @@ def main() -> None:
201
189
  # Validate version progression
202
190
  validate_version_progression(current_version, new_version)
203
191
 
204
- # Confirm with user
205
- print(f"\nThis will create tag v{new_version}")
206
- print("Make sure you have:")
207
- print(" 1. Updated CHANGELOG.rst")
208
- print(" 2. Committed all changes")
209
- print(" 3. Run tests and linting")
210
- response = input("\nProceed with version bump? (yes/no): ")
211
-
212
- if response.lower() != "yes":
213
- print("Version bump cancelled.")
214
- sys.exit(0)
215
-
216
192
  # Create the tag
193
+ print(f"\nCreating tag v{new_version}...")
217
194
  create_tag(new_version)
218
195
 
219
196
  print("\n[OK] Version bump complete!")
@@ -33,7 +33,7 @@ package_dir =
33
33
  python_requires = >=3.9
34
34
  install_requires =
35
35
  aiohttp>=3.8.0
36
- awsiotsdk>=1.25.0
36
+ awsiotsdk>=1.26.0
37
37
 
38
38
  [options.packages.find]
39
39
  where = src
@@ -367,7 +367,7 @@ class NavienAuthClient:
367
367
  async def __aenter__(self) -> "NavienAuthClient":
368
368
  """Async context manager entry."""
369
369
  if self._owned_session:
370
- self._session = aiohttp.ClientSession(timeout=self.timeout)
370
+ self._session = self._create_session()
371
371
 
372
372
  # Check if we have valid stored tokens
373
373
  if self._auth_response and self._auth_response.tokens:
@@ -394,10 +394,24 @@ class NavienAuthClient:
394
394
  if self._owned_session and self._session:
395
395
  await self._session.close()
396
396
 
397
+ def _create_session(self) -> aiohttp.ClientSession:
398
+ """Create an aiohttp ClientSession with ThreadedResolver.
399
+
400
+ ThreadedResolver uses Python's built-in socket module for DNS
401
+ resolution, avoiding dependency on c-ares which can fail in
402
+ containerized environments.
403
+
404
+ Returns:
405
+ aiohttp.ClientSession configured with ThreadedResolver
406
+ """
407
+ resolver = aiohttp.ThreadedResolver()
408
+ connector = aiohttp.TCPConnector(resolver=resolver)
409
+ return aiohttp.ClientSession(connector=connector, timeout=self.timeout)
410
+
397
411
  async def _ensure_session(self) -> None:
398
412
  """Ensure we have an active session."""
399
413
  if self._session is None:
400
- self._session = aiohttp.ClientSession(timeout=self.timeout)
414
+ self._session = self._create_session()
401
415
  self._owned_session = True
402
416
 
403
417
  async def sign_in(
@@ -740,8 +754,11 @@ async def refresh_access_token(refresh_token: str) -> AuthTokens:
740
754
  url = f"{API_BASE_URL}{REFRESH_ENDPOINT}"
741
755
  payload = {"refreshToken": refresh_token}
742
756
 
757
+ # Use ThreadedResolver for reliable DNS in containerized environments
758
+ resolver = aiohttp.ThreadedResolver()
759
+ connector = aiohttp.TCPConnector(resolver=resolver)
743
760
  async with (
744
- aiohttp.ClientSession() as session,
761
+ aiohttp.ClientSession(connector=connector) as session,
745
762
  session.post(url, json=payload) as response,
746
763
  ):
747
764
  response_data = await response.json()
@@ -578,6 +578,12 @@ class DeviceStatus:
578
578
  default_value=TemperatureUnit.FAHRENHEIT,
579
579
  )
580
580
  )
581
+ freezeProtectionTempMin: float = field(
582
+ default=43.0, metadata=meta(conversion="add_20")
583
+ )
584
+ freezeProtectionTempMax: float = field(
585
+ default=65.0, metadata=meta(conversion="add_20")
586
+ )
581
587
 
582
588
  @classmethod
583
589
  def from_dict(cls, data: dict[str, Any]) -> "DeviceStatus":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 6.0.1
3
+ Version: 6.0.2
4
4
  Summary: A library for controlling Navien NWP500 Water Heaters via NaviLink
5
5
  Home-page: https://github.com/eman/nwp500-python
6
6
  Author: Emmanuel Levijarvi
@@ -23,7 +23,7 @@ Requires-Python: >=3.9
23
23
  Description-Content-Type: text/x-rst; charset=UTF-8
24
24
  License-File: LICENSE.txt
25
25
  Requires-Dist: aiohttp>=3.8.0
26
- Requires-Dist: awsiotsdk>=1.25.0
26
+ Requires-Dist: awsiotsdk>=1.26.0
27
27
  Provides-Extra: testing
28
28
  Requires-Dist: setuptools; extra == "testing"
29
29
  Requires-Dist: pytest; extra == "testing"
@@ -1,5 +1,5 @@
1
1
  aiohttp>=3.8.0
2
- awsiotsdk>=1.25.0
2
+ awsiotsdk>=1.26.0
3
3
 
4
4
  [dev]
5
5
  ruff>=0.1.0
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