pthelma 1.0.0__cp310-cp310-win32.whl → 2.0.0__cp310-cp310-win32.whl

Sign up to get free protection for your applications and to get access to all the features.
enhydris_cache/cli.py CHANGED
@@ -2,7 +2,6 @@ import configparser
2
2
  import datetime as dt
3
3
  import logging
4
4
  import os
5
- import sys
6
5
  import traceback
7
6
 
8
7
  import click
@@ -81,7 +80,6 @@ class AppConfig:
81
80
  try:
82
81
  self._parse_config()
83
82
  except (OSError, configparser.Error) as e:
84
- sys.stderr.write(str(e))
85
83
  raise click.ClickException(str(e))
86
84
 
87
85
  def _parse_config(self):
evaporation/cli.py CHANGED
@@ -2,7 +2,6 @@ import configparser
2
2
  import datetime as dt
3
3
  import logging
4
4
  import os
5
- import sys
6
5
  import traceback
7
6
  from glob import glob
8
7
  from io import StringIO
@@ -100,7 +99,6 @@ class AppConfig:
100
99
  try:
101
100
  self._parse_config()
102
101
  except (OSError, configparser.Error) as e:
103
- sys.stderr.write(str(e))
104
102
  raise click.ClickException(str(e))
105
103
 
106
104
  def _parse_config(self):
@@ -155,10 +153,10 @@ class AppConfig:
155
153
  self.time_step = s
156
154
 
157
155
  def _check_time_step(self, s):
158
- if s not in ("D", "H"):
156
+ if s not in ("D", "h"):
159
157
  raise WrongValueError(
160
158
  '"{}" is not an appropriate time step; in this version of '
161
- "vaporize, the step must be either D or H.".format(s)
159
+ "vaporize, the step must be either D or h.".format(s)
162
160
  )
163
161
 
164
162
  def _parse_unit_converters(self):
@@ -412,11 +410,11 @@ class ProcessAtPoint:
412
410
  self.pet.unit = "mm"
413
411
  self.pet.timezone = self.timezone
414
412
  self.pet.variable = "Potential Evapotranspiration"
415
- self.pet.precision = 2 if self.config.time_step == "H" else 1
413
+ self.pet.precision = 2 if self.config.time_step == "h" else 1
416
414
  self.pet.location = self.location
417
415
 
418
416
  def _determine_variables_to_use_in_calculation(self):
419
- if self.config.time_step == "H":
417
+ if self.config.time_step == "h":
420
418
  vars = ["temperature", "humidity", "wind_speed", "solar_radiation"]
421
419
  if "pressure" in self.input_timeseries:
422
420
  vars.append("pressure")
@@ -571,7 +569,7 @@ class ProcessSpatial:
571
569
  self._read_input_geofile(var, timestamp)
572
570
 
573
571
  def _get_variables(self):
574
- if self.config.time_step == "H":
572
+ if self.config.time_step == "h":
575
573
  return {
576
574
  "temperature",
577
575
  "humidity",
@@ -45,7 +45,7 @@ class PenmanMonteith(object):
45
45
  self.unit_converters = unit_converters
46
46
 
47
47
  def calculate(self, **kwargs):
48
- if self.time_step == "H":
48
+ if self.time_step == "h":
49
49
  return self.calculate_hourly(**kwargs)
50
50
  elif self.time_step == "D":
51
51
  return self.calculate_daily(**kwargs)
haggregate/__init__.py CHANGED
@@ -1,5 +1,2 @@
1
1
  from .haggregate import * # NOQA
2
2
  from .regularize import * # NOQA
3
-
4
- __author__ = """Antonis Christofides"""
5
- __email__ = "antonis@antonischristofides.com"
haggregate/haggregate.py CHANGED
@@ -106,7 +106,7 @@ class SourceTimeseries(HTimeseries):
106
106
  )
107
107
  except ValueError:
108
108
  raise CannotInferFrequency()
109
- first_timestamp = (current_range[0] - pd.Timedelta("1S")).floor(target_step)
109
+ first_timestamp = (current_range[0] - pd.Timedelta("1s")).floor(target_step)
110
110
  end_timestamp = current_range[-1].ceil(target_step)
111
111
  new_range = pd.date_range(first_timestamp, end_timestamp, freq=self.freq)
112
112
  self.data = self.data.reindex(new_range)
@@ -116,8 +116,8 @@ class AggregatedTimeseries(HTimeseries):
116
116
  def set_metadata(self, source_timeseries):
117
117
  for attr in attrs:
118
118
  setattr(self, attr, getattr(source_timeseries, attr, None))
119
- if self.time_step not in ("1H", "1D"):
120
- raise AggregateError("The target step can currently only be 1H or 1D")
119
+ if self.time_step not in ("1h", "1D"):
120
+ raise AggregateError("The target step can currently only be 1h or 1D")
121
121
  if hasattr(source_timeseries, "title"):
122
122
  self.title = "Aggregated " + source_timeseries.title
123
123
  if hasattr(source_timeseries, "comment"):
@@ -140,7 +140,7 @@ class AggregatedTimeseries(HTimeseries):
140
140
 
141
141
 
142
142
  def _get_offset_in_minutes(timestamp_offset):
143
- m = re.match(r"(-?)(\d*)(T|min)$", timestamp_offset)
143
+ m = re.match(r"(-?)(\d*)min$", timestamp_offset)
144
144
  if not m:
145
145
  raise AggregateError(
146
146
  "The target timestamp offset can currently only be a number of minutes "
Binary file
hspatial/cli.py CHANGED
@@ -2,7 +2,6 @@ import configparser
2
2
  import datetime as dt
3
3
  import logging
4
4
  import os
5
- import sys
6
5
  import traceback
7
6
  from glob import glob
8
7
  from io import StringIO
@@ -100,7 +99,7 @@ class App:
100
99
  0, iso8601.parse_date(datestring, default_timezone=timezone)
101
100
  )
102
101
  except iso8601.ParseError as e:
103
- raise iso8601.ParseError(
102
+ raise click.ClickException(
104
103
  str(e)
105
104
  + " (file {}, {} lines from the end)".format(filename, i + 1)
106
105
  )
@@ -146,7 +145,7 @@ class App:
146
145
  """
147
146
  Determine date_fmt based on time series time step.
148
147
  """
149
- if self._time_step.endswith("min") or self._time_step.endswith("H"):
148
+ if self._time_step.endswith("min") or self._time_step.endswith("h"):
150
149
  return "%Y-%m-%d %H:%M%z"
151
150
  elif self._time_step.endswith("D"):
152
151
  return "%Y-%m-%d"
@@ -221,7 +220,6 @@ class AppConfig:
221
220
  try:
222
221
  self._parse_config()
223
222
  except (OSError, configparser.Error) as e:
224
- sys.stderr.write(str(e))
225
223
  raise click.ClickException(str(e))
226
224
 
227
225
  def _parse_config(self):
pthelma/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.0.0'
16
- __version_tuple__ = version_tuple = (1, 0, 0)
15
+ __version__ = version = '2.0.0'
16
+ __version_tuple__ = version_tuple = (2, 0, 0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pthelma
3
- Version: 1.0.0
3
+ Version: 2.0.0
4
4
  Summary: Utilities for hydrological and meteorological time series processing
5
5
  Author-email: Antonis Christofides <antonis@antonischristofides.com>
6
6
  Maintainer-email: Antonis Christofides <antonis@antonischristofides.com>
@@ -34,9 +34,9 @@ Requires-Dist: gdal<4,>=1.10; extra == "all"
34
34
  pthelma
35
35
  =======
36
36
 
37
- .. image:: https://github.com/openmeteo/pthelma/actions/workflows/run-tests-automatically.yml/badge.svg
37
+ .. image:: https://github.com/openmeteo/pthelma/actions/workflows/run-tests.yml/badge.svg
38
38
  :alt: Build button
39
- :target: https://github.com/openmeteo/pthelma/actions/workflows/run-tests-automatically.yml
39
+ :target: https://github.com/openmeteo/pthelma/actions/workflows/run-tests.yml
40
40
 
41
41
  .. image:: https://codecov.io/github/openmeteo/pthelma/coverage.svg?branch=master
42
42
  :alt: Coverage
@@ -45,10 +45,6 @@ pthelma
45
45
  .. image:: https://img.shields.io/pypi/v/pthelma.svg
46
46
  :target: https://pypi.python.org/pypi/pthelma
47
47
 
48
- .. image:: https://pyup.io/repos/github/openmeteo/pthelma/shield.svg
49
- :target: https://pyup.io/repos/github/openmeteo/pthelma/
50
- :alt: Updates
51
-
52
48
  A Python library with utilities for hydrological and meteorological time
53
49
  series processing.
54
50
 
@@ -0,0 +1,30 @@
1
+ enhydris_api_client/__init__.py,sha256=TQQD2zgSAl-7eXOOHjgvHEqMW1D3GSmf9nI9cmR31c8,9592
2
+ enhydris_cache/__init__.py,sha256=n3mAFeXUINSGpRkO-ZL1tkxyAuEabnMdlNPpmKur0AU,157
3
+ enhydris_cache/cli.py,sha256=BEFBYdrfdJbEUyeY87BLWw7igKlfB2qzkpopPZrAIUQ,4743
4
+ enhydris_cache/enhydris_cache.py,sha256=Mvpk2JxF6hSgk0qmGWEVFjIAoehFbdK41LyBJjAYhZc,2691
5
+ evaporation/__init__.py,sha256=ekZNT_-GEt1lDmwbX_RIaOvHSRjX05iVWeupaxZV75Y,126
6
+ evaporation/cli.py,sha256=06TIjd6Jr_p6YjM0bOGgKqSufj5exvHXElSEAvleaNA,28392
7
+ evaporation/evaporation.py,sha256=87kNBAplijIEk_kzPWHBOYtWDWqNAK7_oZL_U2gaV9g,16702
8
+ haggregate/__init__.py,sha256=KfFmAI6bvEsnaflhq9pK1nzOdoyySFiXnw9o1-Nmcus,70
9
+ haggregate/cli.py,sha256=Ou2zQRnj9ZsNM6it7HFGLavrdGaExZDartlPGPuaNLc,3363
10
+ haggregate/haggregate.py,sha256=nfOUW_M4q_qrS-rEQh7lJks9G9724xV6oX0ayF_Pmdw,5196
11
+ haggregate/regularize.cp310-win32.pyd,sha256=toHZUXrk74qr-JXDtkimcT8WjSqHjnuPeC85rEne6_E,91136
12
+ haggregate/regularize.pyx,sha256=aaxx-3wJtuo51G9n7GIzFb2qt6m7rXuoAES9zoI7Zcw,5800
13
+ hspatial/__init__.py,sha256=dlTLFy7IqQwun4nz01qtYMQbU9kBmHEKoOXGIgCRf9c,123
14
+ hspatial/cli.py,sha256=1iAY9-ATkO_xzlGCC_WSw42S9w2ca0uRLSxWG3WIat8,10638
15
+ hspatial/hspatial.py,sha256=fv9xia23D9W5kXG00XlDrXYMF8KotUlIfL9ZjuGEyPg,16348
16
+ hspatial/test.py,sha256=_YZzfgEtlCJmOGOdnxwpsPC48EBZ_QaKKBdyJRmg014,1050
17
+ htimeseries/__init__.py,sha256=by-XbIt4Q-lHHKlHEcgXLi7AjEGNv0VGNmM0ZMl8L7M,75
18
+ htimeseries/htimeseries.py,sha256=y8rIFabSCdBwmS3vWGEuyRMtQvIZy81jqg304-kIg3w,19771
19
+ htimeseries/timezone_utils.py,sha256=jTWhKljHXQuD_drNgfI2ytD6atf6dwnsL4qCVK8wpgA,1282
20
+ pthelma/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ pthelma/_version.py,sha256=hje97DjNnjjzdk2AwQsDgJXvORhASGZ-8JywrlJVLIw,427
22
+ rocc/__init__.py,sha256=KfFB3rfJXgYL4HnVa7MNYcotG3am-CajVvh65gm9v_s,281
23
+ rocc/calculation.cp310-win32.pyd,sha256=MP6kMcCnkySzkOiLedLkvcPLmDwc9yRrbzjQGMxerbM,86528
24
+ rocc/calculation.pyx,sha256=tWWSB1u2JTCnYv6mb_YwK97vPrL7JT_ymx_5TlmG_9Y,5938
25
+ pthelma-2.0.0.dist-info/LICENSE.rst,sha256=v1ndaPoHsiO-q3mq-ibN0HooeO-LCZBvKPfwlJRIMWs,1379
26
+ pthelma-2.0.0.dist-info/METADATA,sha256=QnFd03qXLTgir1qyXPxoob8pN0Uxp5Zu4U3hm6LyD_8,2018
27
+ pthelma-2.0.0.dist-info/WHEEL,sha256=IJJUxdZyjrGcLR3XjvKOCbzRR0h6oB7KSRhSaDzEI7U,97
28
+ pthelma-2.0.0.dist-info/entry_points.txt,sha256=Kh2gKKRhObmmOASJWIwX9dDEZQ5htc3ftBlqeoGer-s,155
29
+ pthelma-2.0.0.dist-info/top_level.txt,sha256=kWL0AYuVd_5KzKWRBkAG576ag46gKbojnWdeH_c-bzo,92
30
+ pthelma-2.0.0.dist-info/RECORD,,
@@ -5,3 +5,4 @@ haggregate
5
5
  hspatial
6
6
  htimeseries
7
7
  pthelma
8
+ rocc
rocc/__init__.py ADDED
@@ -0,0 +1,9 @@
1
+ from collections import namedtuple
2
+
3
+ from .calculation import Rocc
4
+
5
+ Threshold = namedtuple("Threshold", ["delta_t", "allowed_diff"])
6
+
7
+
8
+ def rocc(*, timeseries, thresholds, symmetric=False, flag="TEMPORAL"):
9
+ return Rocc(timeseries, thresholds, symmetric, flag).execute()
Binary file
rocc/calculation.pyx ADDED
@@ -0,0 +1,182 @@
1
+ # cython: language_level=3
2
+ import datetime as dt
3
+
4
+ cimport numpy as np
5
+ from cpython cimport array
6
+
7
+ import numpy as np
8
+ import pandas as pd
9
+
10
+
11
+ class Rocc:
12
+ def __init__(self, timeseries, thresholds, symmetric, flag):
13
+ self.htimeseries = timeseries
14
+ self.thresholds = thresholds
15
+ self.symmetric = symmetric
16
+ self.flag = flag or ""
17
+
18
+ def execute(self):
19
+ self._transform_thresholds()
20
+ self._transform_to_plain_numpy()
21
+ failures = self._do_actual_job()
22
+ self._transform_to_pandas()
23
+ return failures
24
+
25
+ def _transform_thresholds(self):
26
+ threshold_deltas = array.array("l")
27
+ threshold_allowed_diffs = array.array("d")
28
+
29
+ for threshold in self.thresholds:
30
+ delta_t = int(self._get_delta_t_transformed(threshold.delta_t))
31
+ threshold_deltas.append(delta_t)
32
+ threshold_allowed_diffs.append(threshold.allowed_diff)
33
+ self.threshold_deltas = threshold_deltas
34
+ self.threshold_allowed_diffs = threshold_allowed_diffs
35
+
36
+ def _get_delta_t_transformed(self, delta_t):
37
+ if not delta_t[0].isdigit():
38
+ delta_t = "1" + delta_t
39
+ return pd.Timedelta(delta_t).to_timedelta64()
40
+
41
+ def _transform_to_plain_numpy(self):
42
+ flag_lengths = self.htimeseries.data["flags"].str.len()
43
+ max_flag_length = 0 if flag_lengths.empty else max(flag_lengths)
44
+ flags_dtype = "U" + str(max_flag_length + 1 + len(self.flag))
45
+ self.ts_index = self.htimeseries.data.index.values.astype(long)
46
+ self.ts_values = self.htimeseries.data["value"].values
47
+ self.ts_flags = self.htimeseries.data["flags"].values.astype(flags_dtype)
48
+ try:
49
+ utc_offset = self.htimeseries.data.index.tz.utcoffset(dt.datetime.now())
50
+ except AttributeError:
51
+ utc_offset = dt.timedelta(0)
52
+ self.ts_utc_offset_minutes = int(utc_offset.total_seconds() / 60)
53
+
54
+ def _do_actual_job(self):
55
+ return _perform_rocc(
56
+ self.ts_index,
57
+ self.ts_values,
58
+ self.ts_flags,
59
+ self.ts_utc_offset_minutes,
60
+ list(self.thresholds),
61
+ self.threshold_deltas,
62
+ self.threshold_allowed_diffs,
63
+ self.symmetric,
64
+ self.flag,
65
+ )
66
+
67
+ def _transform_to_pandas(self):
68
+ self.htimeseries.data = pd.DataFrame(
69
+ index=self.htimeseries.data.index,
70
+ columns=["value", "flags"],
71
+ data=np.vstack((self.ts_values, self.ts_flags)).transpose(),
72
+ )
73
+ self.htimeseries.data["value"] = self.htimeseries.data["value"].astype(np.float64)
74
+
75
+
76
+ # IMPORTANT: There's some plain Python in the Cython below. Specifically, there are some
77
+ # Python lists and some places with undeclared variables. These are only used when a
78
+ # failure is found. Given that failures should be very few, this should not affect the
79
+ # overall speed. But I'm not really a Cython expert and I don't know exactly how it
80
+ # works.
81
+
82
+
83
+ def _perform_rocc(
84
+ np.ndarray ts_index,
85
+ np.ndarray ts_values,
86
+ np.ndarray ts_flags,
87
+ int ts_utc_offset_minutes,
88
+ list thresholds,
89
+ array.array threshold_deltas,
90
+ array.array threshold_allowed_diffs,
91
+ int symmetric,
92
+ str flag,
93
+ ):
94
+ cdef int i, record_fails_check
95
+ cdef list failures = []
96
+
97
+ for i in range(ts_index.size):
98
+ record_fails_check = _record_fails_check(
99
+ i,
100
+ ts_index,
101
+ ts_values,
102
+ ts_utc_offset_minutes,
103
+ thresholds,
104
+ threshold_deltas,
105
+ threshold_allowed_diffs,
106
+ symmetric,
107
+ failures,
108
+ )
109
+ if record_fails_check and flag:
110
+ _add_flag(i, ts_flags, flag)
111
+ return failures
112
+
113
+
114
+ def _add_flag(int i, np.ndarray ts_flags, str flag):
115
+ if ts_flags[i]:
116
+ ts_flags[i] = ts_flags[i] + " "
117
+ ts_flags[i] = ts_flags[i] + flag
118
+
119
+
120
+ def _record_fails_check(
121
+ int record_index,
122
+ np.ndarray ts_index,
123
+ np.ndarray ts_values,
124
+ int ts_utc_offset_minutes,
125
+ list thresholds,
126
+ array.array threshold_deltas,
127
+ array.array threshold_allowed_diffs,
128
+ int symmetric,
129
+ list failures,
130
+ ):
131
+ cdef int ti
132
+ cdef double diff
133
+
134
+ for ti in range(len(threshold_deltas)):
135
+ diff = _record_fails_threshold(
136
+ record_index,
137
+ threshold_deltas[ti],
138
+ threshold_allowed_diffs[ti],
139
+ ts_index,
140
+ ts_values,
141
+ symmetric,
142
+ )
143
+ if diff:
144
+ timestamp = ts_index[record_index].item()
145
+ datestr = str(
146
+ np.datetime64(timestamp, "ns") + np.timedelta64(ts_utc_offset_minutes, "m")
147
+ )[:16]
148
+ diffsign = '+' if diff > 0 else ''
149
+ thresholdsign = '-' if diff < 0 else ''
150
+ cmpsign = '>' if diff > 0 else '<'
151
+ failures.append(
152
+ f"{datestr} {diffsign}{diff} in {thresholds[ti].delta_t} "
153
+ f"({cmpsign} {thresholdsign}{threshold_allowed_diffs[ti]})"
154
+ )
155
+ return True
156
+ return False
157
+
158
+
159
+ def _record_fails_threshold(
160
+ int record_index,
161
+ long threshold_delta,
162
+ double threshold_allowed_diff,
163
+ np.ndarray ts_index,
164
+ np.ndarray ts_values,
165
+ int symmetric,
166
+ ):
167
+ cdef double current_value = ts_values[record_index]
168
+ cdef long current_timestamp = ts_index[record_index]
169
+ cdef int i, fails
170
+ cdef double diff;
171
+
172
+ for i in range(record_index - 1, -1, -1):
173
+ if current_timestamp - ts_index[i] > threshold_delta:
174
+ return False
175
+ diff = current_value - ts_values[i];
176
+ fails = (
177
+ diff > threshold_allowed_diff
178
+ or (symmetric and diff < -threshold_allowed_diff)
179
+ )
180
+ if fails:
181
+ return diff
182
+ return False
@@ -1,27 +0,0 @@
1
- enhydris_api_client/__init__.py,sha256=TQQD2zgSAl-7eXOOHjgvHEqMW1D3GSmf9nI9cmR31c8,9592
2
- enhydris_cache/__init__.py,sha256=n3mAFeXUINSGpRkO-ZL1tkxyAuEabnMdlNPpmKur0AU,157
3
- enhydris_cache/cli.py,sha256=aqlcp0zXVNSmMAVfjVz_2ri6V7pHiSRPthO4y1LCdf4,4793
4
- enhydris_cache/enhydris_cache.py,sha256=Mvpk2JxF6hSgk0qmGWEVFjIAoehFbdK41LyBJjAYhZc,2691
5
- evaporation/__init__.py,sha256=ekZNT_-GEt1lDmwbX_RIaOvHSRjX05iVWeupaxZV75Y,126
6
- evaporation/cli.py,sha256=4HdusE0Trt81HsNlClvlrAu-493GtGa6bn_KuV4opzs,28442
7
- evaporation/evaporation.py,sha256=twTkFsuI5QRcof1XbnOG5vhnEMgrA2zXYHxYleKV5-k,16702
8
- haggregate/__init__.py,sha256=rqRWh8aJ44DEnAnxfwOWfqFKMZGkvewH81SH5RcSGGM,160
9
- haggregate/cli.py,sha256=Ou2zQRnj9ZsNM6it7HFGLavrdGaExZDartlPGPuaNLc,3363
10
- haggregate/haggregate.py,sha256=pXzT2z_LshxFD9USiV1je-swAj8n-iY7fetxVO8XJ-A,5200
11
- haggregate/regularize.cp310-win32.pyd,sha256=KmtJqW086sQcmRL7cdR14mN1-2HfN3ypevli28heH4U,91136
12
- haggregate/regularize.pyx,sha256=aaxx-3wJtuo51G9n7GIzFb2qt6m7rXuoAES9zoI7Zcw,5800
13
- hspatial/__init__.py,sha256=dlTLFy7IqQwun4nz01qtYMQbU9kBmHEKoOXGIgCRf9c,123
14
- hspatial/cli.py,sha256=alkp-IxwMBErwL2sjZmSLWeRG3DXr0c4A4GV0O0Fk0o,10686
15
- hspatial/hspatial.py,sha256=fv9xia23D9W5kXG00XlDrXYMF8KotUlIfL9ZjuGEyPg,16348
16
- hspatial/test.py,sha256=_YZzfgEtlCJmOGOdnxwpsPC48EBZ_QaKKBdyJRmg014,1050
17
- htimeseries/__init__.py,sha256=by-XbIt4Q-lHHKlHEcgXLi7AjEGNv0VGNmM0ZMl8L7M,75
18
- htimeseries/htimeseries.py,sha256=y8rIFabSCdBwmS3vWGEuyRMtQvIZy81jqg304-kIg3w,19771
19
- htimeseries/timezone_utils.py,sha256=jTWhKljHXQuD_drNgfI2ytD6atf6dwnsL4qCVK8wpgA,1282
20
- pthelma/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- pthelma/_version.py,sha256=dJiaCltLFgci5aFfMnwDnv8BAhveHDCtWSHatUxAG1Q,427
22
- pthelma-1.0.0.dist-info/LICENSE.rst,sha256=v1ndaPoHsiO-q3mq-ibN0HooeO-LCZBvKPfwlJRIMWs,1379
23
- pthelma-1.0.0.dist-info/METADATA,sha256=gBNt0yEQnHNL-PA_mwPtpStaW7yHwk677_IJVF4fGdQ,2209
24
- pthelma-1.0.0.dist-info/WHEEL,sha256=IJJUxdZyjrGcLR3XjvKOCbzRR0h6oB7KSRhSaDzEI7U,97
25
- pthelma-1.0.0.dist-info/entry_points.txt,sha256=Kh2gKKRhObmmOASJWIwX9dDEZQ5htc3ftBlqeoGer-s,155
26
- pthelma-1.0.0.dist-info/top_level.txt,sha256=tmHh9NPLZEWJBUT19b-ZqJmAOG6QAfyM_Z3d-B7XS7w,87
27
- pthelma-1.0.0.dist-info/RECORD,,