wwvb 3.0.6__py3-none-any.whl → 3.0.8__py3-none-any.whl

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.
uwwvb.py CHANGED
@@ -16,9 +16,7 @@ always_mark = set((0, 9, 19, 29, 39, 49, 59))
16
16
  always_zero = set((4, 10, 11, 14, 20, 21, 34, 35, 44, 54))
17
17
  bcd_weights = (1, 2, 4, 8, 10, 20, 40, 80, 100, 200, 400, 800)
18
18
 
19
- WWVBMinute = namedtuple(
20
- "WWVBMinute", ["year", "days", "hour", "minute", "dst", "ut1", "ls", "ly"]
21
- )
19
+ WWVBMinute = namedtuple("WWVBMinute", ["year", "days", "hour", "minute", "dst", "ut1", "ls", "ly"])
22
20
 
23
21
 
24
22
  class WWVBDecoder:
@@ -30,7 +28,9 @@ class WWVBDecoder:
30
28
  self.state = 1
31
29
 
32
30
  def update(self, value: int) -> list[int] | None:
33
- """Update the _state machine when a new symbol is received. If a possible complete _minute is received, return it; otherwise, return None"""
31
+ """Update the _state machine when a new symbol is received.
32
+
33
+ If a possible complete _minute is received, return it; otherwise, return None"""
34
34
  result = None
35
35
  if self.state == 1:
36
36
  self.minute = []
@@ -87,7 +87,7 @@ def get_am_bcd(seq: list[int], *poslist: int) -> int | None:
87
87
  return result
88
88
 
89
89
 
90
- def decode_wwvb( # pylint: disable=too-many-return-statements
90
+ def decode_wwvb(
91
91
  t: list[int] | None,
92
92
  ) -> WWVBMinute | None:
93
93
  """Convert a received minute of wwvb symbols to a WWVBMinute. Returns None if any error is detected."""
wwvb/__init__.py CHANGED
@@ -19,7 +19,7 @@ from .tz import Mountain
19
19
  HOUR = datetime.timedelta(seconds=3600)
20
20
  SECOND = datetime.timedelta(seconds=1)
21
21
  DateOrDatetime = TypeVar("DateOrDatetime", datetime.date, datetime.datetime)
22
- T = TypeVar("T") # pylint: disable=invalid-name
22
+ T = TypeVar("T")
23
23
 
24
24
 
25
25
  def require(x: Optional[T]) -> T:
@@ -49,9 +49,7 @@ def _maybe_warn_update(dt: datetime.date) -> None:
49
49
  # prospective available now.
50
50
  today = datetime.date.today()
51
51
  if _date(dt) < today + datetime.timedelta(days=330):
52
- warnings.warn(
53
- "Note: Running `updateiers` may provide better DUT1 and LS information"
54
- )
52
+ warnings.warn("Note: Running `updateiers` may provide better DUT1 and LS information")
55
53
 
56
54
 
57
55
  def get_dut1(dt: DateOrDatetime, *, warn_outdated: bool = True) -> float:
@@ -110,9 +108,7 @@ def is_dst_change_day(t: datetime.date, tz: datetime.tzinfo = Mountain) -> bool:
110
108
  return isdst(t, tz) != isdst(t + datetime.timedelta(1), tz)
111
109
 
112
110
 
113
- def get_dst_change_hour(
114
- t: DateOrDatetime, tz: datetime.tzinfo = Mountain
115
- ) -> Optional[int]:
111
+ def get_dst_change_hour(t: DateOrDatetime, tz: datetime.tzinfo = Mountain) -> Optional[int]:
116
112
  """Return the hour when DST changes"""
117
113
  lt0 = datetime.datetime(t.year, t.month, t.day, hour=0, tzinfo=tz)
118
114
  dst0 = lt0.dst()
@@ -291,7 +287,9 @@ def extract_bit(v: int, p: int) -> bool:
291
287
 
292
288
 
293
289
  def hamming_parity(value: int) -> int:
294
- """Compute the "hamming parity" of a 26-bit number, such as the minute-of-century [See Enhanced WWVB Broadcast Format 4.3]"""
290
+ """Compute the "hamming parity" of a 26-bit number, such as the minute-of-century
291
+
292
+ For more details, see Enhanced WWVB Broadcast Format 4.3"""
295
293
  parity = 0
296
294
  for i in range(4, -1, -1):
297
295
  bit = 0
@@ -324,7 +322,9 @@ _WWVBMinute = collections.namedtuple("_WWVBMinute", "year days hour min dst ut1
324
322
 
325
323
 
326
324
  class WWVBMinute(_WWVBMinute):
327
- """Uniquely identifies a minute of time in the WWVB system. To use ut1 and ls information from IERS, create a WWVBMinuteIERS value instead."""
325
+ """Uniquely identifies a minute of time in the WWVB system.
326
+
327
+ To use ut1 and ls information from IERS, create a WWVBMinuteIERS value instead."""
328
328
 
329
329
  year: int
330
330
  hour: int
@@ -336,7 +336,7 @@ class WWVBMinute(_WWVBMinute):
336
336
 
337
337
  epoch: int = 1970
338
338
 
339
- def __new__( # pylint: disable=too-many-arguments
339
+ def __new__(
340
340
  cls,
341
341
  year: int,
342
342
  days: int,
@@ -391,7 +391,11 @@ class WWVBMinute(_WWVBMinute):
391
391
 
392
392
  def __str__(self) -> str:
393
393
  """Implement str()"""
394
- return f"year={self.year:4d} days={self.days:03d} hour={self.hour:02d} min={self.min:02d} dst={self.dst} ut1={self.ut1} ly={int(self.ly)} ls={int(self.ls)}"
394
+ return (
395
+ f"year={self.year:4d} days={self.days:03d} hour={self.hour:02d} "
396
+ f"min={self.min:02d} dst={self.dst} ut1={self.ut1} ly={int(self.ly)} "
397
+ f"ls={int(self.ls)}"
398
+ )
395
399
 
396
400
  def as_datetime_utc(self) -> datetime.datetime:
397
401
  """Convert to a UTC datetime"""
@@ -401,9 +405,7 @@ class WWVBMinute(_WWVBMinute):
401
405
 
402
406
  as_datetime = as_datetime_utc
403
407
 
404
- def as_datetime_local(
405
- self, standard_time_offset: int = 7 * 3600, dst_observed: bool = True
406
- ) -> datetime.datetime:
408
+ def as_datetime_local(self, standard_time_offset: int = 7 * 3600, dst_observed: bool = True) -> datetime.datetime:
407
409
  """Convert to a local datetime according to the DST bits"""
408
410
  u = self.as_datetime_utc()
409
411
  offset = datetime.timedelta(seconds=-standard_time_offset)
@@ -471,12 +473,7 @@ class WWVBMinute(_WWVBMinute):
471
473
  century = (self.year // 100) * 100
472
474
  # note: This relies on timedelta seconds never including leapseconds!
473
475
  return (
474
- int(
475
- (
476
- self.as_datetime()
477
- - datetime.datetime(century, 1, 1, tzinfo=datetime.timezone.utc)
478
- ).total_seconds()
479
- )
476
+ int((self.as_datetime() - datetime.datetime(century, 1, 1, tzinfo=datetime.timezone.utc)).total_seconds())
480
477
  // 60
481
478
  )
482
479
 
@@ -497,9 +494,7 @@ class WWVBMinute(_WWVBMinute):
497
494
  t.am[36] = t.am[38] = AmplitudeModulation(ut1_sign)
498
495
  t.am[37] = AmplitudeModulation(not ut1_sign)
499
496
  t._put_am_bcd(abs(self.ut1) // 100, 40, 41, 42, 43)
500
- t._put_am_bcd(
501
- self.year, 45, 46, 47, 48, 50, 51, 52, 53
502
- ) # Implicitly discards all but lowest 2 digits of year
497
+ t._put_am_bcd(self.year, 45, 46, 47, 48, 50, 51, 52, 53) # Implicitly discards all but lowest 2 digits of year
503
498
  t.am[55] = AmplitudeModulation(self.ly)
504
499
  t.am[56] = AmplitudeModulation(self.ls)
505
500
  t._put_am_bcd(self.dst, 57, 58)
@@ -539,9 +534,7 @@ class WWVBMinute(_WWVBMinute):
539
534
  for i in range(60):
540
535
  t._put_pm_bit(i, full_seq[i + offset])
541
536
 
542
- def fill_pm_timecode_regular( # pylint: disable=too-many-statements
543
- self, t: "WWVBTimecode"
544
- ) -> None:
537
+ def fill_pm_timecode_regular(self, t: "WWVBTimecode") -> None:
545
538
  """Except during minutes 10..15 and 40..45, the amplitude signal holds 'regular information'"""
546
539
  t._put_pm_bin(0, 13, SYNC_T)
547
540
 
@@ -604,24 +597,18 @@ class WWVBMinute(_WWVBMinute):
604
597
  else:
605
598
  self.fill_pm_timecode_regular(t)
606
599
 
607
- def next_minute(
608
- self, newut1: Optional[int] = None, newls: Optional[bool] = None
609
- ) -> "WWVBMinute":
600
+ def next_minute(self, newut1: Optional[int] = None, newls: Optional[bool] = None) -> "WWVBMinute":
610
601
  """Return an object representing the next minute"""
611
602
  d = self.as_datetime() + datetime.timedelta(minutes=1)
612
603
  return self.from_datetime(d, newut1, newls, self)
613
604
 
614
- def previous_minute(
615
- self, newut1: Optional[int] = None, newls: Optional[bool] = None
616
- ) -> "WWVBMinute":
605
+ def previous_minute(self, newut1: Optional[int] = None, newls: Optional[bool] = None) -> "WWVBMinute":
617
606
  """Return an object representing the previous minute"""
618
607
  d = self.as_datetime() - datetime.timedelta(minutes=1)
619
608
  return self.from_datetime(d, newut1, newls, self)
620
609
 
621
610
  @classmethod
622
- def _get_dut1_info( # pylint: disable=unused-argument
623
- cls: type, year: int, days: int, old_time: "Optional[WWVBMinute]" = None
624
- ) -> Tuple[int, bool]:
611
+ def _get_dut1_info(cls: type, year: int, days: int, old_time: "Optional[WWVBMinute]" = None) -> Tuple[int, bool]:
625
612
  """Return the DUT1 information for a given day, possibly propagating information from a previous timestamp"""
626
613
  if old_time is not None:
627
614
  if old_time.minute_length() != 60:
@@ -673,9 +660,7 @@ class WWVBMinute(_WWVBMinute):
673
660
  return cls(u.tm_year, u.tm_yday, u.tm_hour, u.tm_min, ut1=newut1, ls=newls)
674
661
 
675
662
  @classmethod
676
- def from_timecode_am( # pylint: disable=too-many-return-statements
677
- cls, t: "WWVBTimecode"
678
- ) -> Optional["WWVBMinute"]:
663
+ def from_timecode_am(cls, t: "WWVBTimecode") -> Optional["WWVBMinute"]:
679
664
  """Construct a WWVBMinute from a WWVBTimecode"""
680
665
  for i in (0, 9, 19, 29, 39, 49, 59):
681
666
  if t.am[i] != AmplitudeModulation.MARK:
@@ -717,9 +702,7 @@ class WWVBMinuteIERS(WWVBMinute):
717
702
  """A WWVBMinute that uses a database of DUT1 information"""
718
703
 
719
704
  @classmethod
720
- def _get_dut1_info(
721
- cls, year: int, days: int, old_time: Optional[WWVBMinute] = None
722
- ) -> Tuple[int, bool]:
705
+ def _get_dut1_info(cls, year: int, days: int, old_time: Optional[WWVBMinute] = None) -> Tuple[int, bool]:
723
706
  d = datetime.datetime(year, 1, 1) + datetime.timedelta(days - 1)
724
707
  return int(round(get_dut1(d) * 10)) * 100, isls(d)
725
708
 
@@ -759,11 +742,14 @@ class WWVBTimecode:
759
742
  phase: List[PhaseModulation]
760
743
 
761
744
  def __init__(self, sz: int) -> None:
762
- self.am = [AmplitudeModulation.UNSET] * sz # pylint: disable=invalid-name
745
+ self.am = [AmplitudeModulation.UNSET] * sz
763
746
  self.phase = [PhaseModulation.UNSET] * sz
764
747
 
765
748
  def _get_am_bcd(self, *poslist: int) -> Optional[int]:
766
- """Convert the bits seq[positions[0]], ... seq[positions[len(positions-1)]] [in MSB order] from BCD to decimal"""
749
+ """Convert AM data to BCD
750
+
751
+ The the bits ``self.am[poslist[i]]`` in MSB order are converted from
752
+ BCD to integer"""
767
753
  pos = reversed(poslist)
768
754
  val = [bool(self.am[p]) for p in pos]
769
755
  result = 0
@@ -779,7 +765,11 @@ class WWVBTimecode:
779
765
  return result
780
766
 
781
767
  def _put_am_bcd(self, v: int, *poslist: int) -> None:
782
- """Treating 'poslist' as a sequence of indices, update the AM signal with the value as a BCD number"""
768
+ """Insert BCD coded data into the AM signal
769
+
770
+ The bits at ``self.am[poslist[i]]`` in MSB order are filled with
771
+ the conversion of `v` to BCD
772
+ Treating 'poslist' as a sequence of indices, update the AM signal with the value as a BCD number"""
783
773
  pos = list(poslist)[::-1]
784
774
  for p, b in zip(pos, bcd_bits(v)):
785
775
  if b:
@@ -798,9 +788,7 @@ class WWVBTimecode:
798
788
 
799
789
  def __str__(self) -> str:
800
790
  """implement str()"""
801
- undefined = [
802
- i for i in range(len(self.am)) if self.am[i] == AmplitudeModulation.UNSET
803
- ]
791
+ undefined = [i for i in range(len(self.am)) if self.am[i] == AmplitudeModulation.UNSET]
804
792
  if undefined:
805
793
  warnings.warn(f"am{undefined} is unset")
806
794
 
@@ -841,7 +829,6 @@ styles = {
841
829
  }
842
830
 
843
831
 
844
- # pylint: disable=too-many-arguments
845
832
  def print_timecodes(
846
833
  w: WWVBMinute,
847
834
  minutes: int,
@@ -879,7 +866,6 @@ def print_timecodes(
879
866
  w = w.next_minute()
880
867
 
881
868
 
882
- # pylint: disable=too-many-arguments
883
869
  def print_timecodes_json(
884
870
  w: WWVBMinute,
885
871
  minutes: int,
wwvb/__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 = '3.0.6'
16
- __version_tuple__ = version_tuple = (3, 0, 6)
15
+ __version__ = version = '3.0.8'
16
+ __version_tuple__ = version_tuple = (3, 0, 8)
wwvb/decode.py CHANGED
@@ -23,9 +23,7 @@ import wwvb
23
23
  always_zero = set((4, 10, 11, 14, 20, 21, 34, 35, 44, 54))
24
24
 
25
25
 
26
- def wwvbreceive() -> (
27
- Generator[Optional[wwvb.WWVBTimecode], wwvb.AmplitudeModulation, None]
28
- ): # pylint: disable=too-many-branches
26
+ def wwvbreceive() -> Generator[Optional[wwvb.WWVBTimecode], wwvb.AmplitudeModulation, None]:
29
27
  """A stateful decoder of WWVB signals"""
30
28
  minute: List[wwvb.AmplitudeModulation] = []
31
29
  state = 1
@@ -60,10 +58,7 @@ def wwvbreceive() -> (
60
58
  elif len(minute) % 10 and value == wwvb.AmplitudeModulation.MARK:
61
59
  # print("UNEXPECTED MARK")
62
60
  state = 1
63
- elif (
64
- len(minute) - 1 in always_zero
65
- and value != wwvb.AmplitudeModulation.ZERO
66
- ):
61
+ elif len(minute) - 1 in always_zero and value != wwvb.AmplitudeModulation.ZERO:
67
62
  # print("UNEXPECTED NONZERO")
68
63
  state = 1
69
64
  elif len(minute) == 60:
wwvb/dut1table.py CHANGED
@@ -5,6 +5,7 @@
5
5
  # SPDX-License-Identifier: GPL-3.0-only
6
6
 
7
7
  """Print the table of historical DUT1 values"""
8
+
8
9
  from datetime import timedelta
9
10
  from itertools import groupby
10
11
 
wwvb/gen.py CHANGED
@@ -16,9 +16,7 @@ import dateutil.parser
16
16
  from . import WWVBMinute, WWVBMinuteIERS, print_timecodes, print_timecodes_json, styles
17
17
 
18
18
 
19
- def parse_timespec( # pylint: disable=unused-argument
20
- ctx: Any, param: Any, value: List[str]
21
- ) -> datetime.datetime:
19
+ def parse_timespec(ctx: Any, param: Any, value: List[str]) -> datetime.datetime:
22
20
  """Parse a time specifier from the commandline"""
23
21
  try:
24
22
  if len(value) == 5:
@@ -26,9 +24,7 @@ def parse_timespec( # pylint: disable=unused-argument
26
24
  return datetime.datetime(year, month, day, hour, minute)
27
25
  if len(value) == 4:
28
26
  year, yday, hour, minute = map(int, value)
29
- return datetime.datetime(year, 1, 1, hour, minute) + datetime.timedelta(
30
- days=yday - 1
31
- )
27
+ return datetime.datetime(year, 1, 1, hour, minute) + datetime.timedelta(days=yday - 1)
32
28
  if len(value) == 1:
33
29
  return dateutil.parser.parse(value[0])
34
30
  if len(value) == 0:
@@ -68,9 +64,7 @@ def parse_timespec( # pylint: disable=unused-argument
68
64
  help="Force no leap second at the end of the month (Implies --no-iers)",
69
65
  )
70
66
  @click.option("--dut1", "-d", type=int, help="Force the DUT1 value (Implies --no-iers)")
71
- @click.option(
72
- "--minutes", "-m", default=10, help="Number of minutes to show (default: 10)"
73
- )
67
+ @click.option("--minutes", "-m", default=10, help="Number of minutes to show (default: 10)")
74
68
  @click.option(
75
69
  "--style",
76
70
  default="default",
@@ -91,7 +85,6 @@ def parse_timespec( # pylint: disable=unused-argument
91
85
  help="Modulation to show (default: amplitude)",
92
86
  )
93
87
  @click.argument("timespec", type=str, nargs=-1, callback=parse_timespec)
94
- # pylint: disable=too-many-arguments, too-many-locals
95
88
  def main(
96
89
  iers: bool,
97
90
  leap_second: bool,
@@ -127,9 +120,7 @@ def main(
127
120
  if style == "json":
128
121
  print_timecodes_json(w, minutes, channel, file=sys.stdout)
129
122
  else:
130
- print_timecodes(
131
- w, minutes, channel, style, all_timecodes=all_timecodes, file=sys.stdout
132
- )
123
+ print_timecodes(w, minutes, channel, style, all_timecodes=all_timecodes, file=sys.stdout)
133
124
 
134
125
 
135
126
  if __name__ == "__main__": # pragma no branch
wwvb/iersdata.py CHANGED
@@ -21,11 +21,9 @@ for location in [
21
21
  filename = os.path.join(location, "wwvbpy_iersdata.py")
22
22
  if os.path.exists(filename):
23
23
  with open(filename, encoding="utf-8") as f:
24
- exec(f.read(), globals(), globals()) # pylint: disable=exec-used
24
+ exec(f.read(), globals(), globals())
25
25
  break
26
26
 
27
- start = datetime.datetime.combine(DUT1_DATA_START, datetime.time()).replace(
28
- tzinfo=datetime.timezone.utc
29
- )
27
+ start = datetime.datetime.combine(DUT1_DATA_START, datetime.time()).replace(tzinfo=datetime.timezone.utc)
30
28
  span = datetime.timedelta(days=len(DUT1_OFFSETS))
31
29
  end = start + span
wwvb/iersdata_dist.py CHANGED
@@ -1,10 +1,9 @@
1
1
  # -*- python3 -*-
2
+ # fmt: off
2
3
  """File generated from public data - not subject to copyright"""
3
4
  # SPDX-FileCopyrightText: Public domain
4
5
  # SPDX-License-Identifier: CC0-1.0
5
- # fmt: off
6
6
  # isort: skip_file
7
- # pylint: disable=invalid-name
8
7
  import datetime
9
8
  __all__ = ['DUT1_DATA_START', 'DUT1_OFFSETS']
10
9
  DUT1_DATA_START = datetime.date(1972, 1, 1)
@@ -35,5 +34,5 @@ DUT1_OFFSETS = str( # 19720101
35
34
  +i*126+h*176+g*97+f*91+e*52+o*116+n*98+m*70+l*133+k*91+j*91 # 20140507
36
35
  +i*77+h*140+g*91+f*84+e*70+d*34+n*72+m*76+l*66+k*53+j*56 # 20160831
37
36
  +i*105+h*77+g*45+q*25+p*63+o*91+n*154+m*105+l*190+k*118 # 20190501
38
- +j*105+i*807+j*376+k*792+l*19+k*18 # 20241102
37
+ +j*105+i*807+j*376+k*775+l*67+k*2+l*6+k*133 # 20250405
39
38
  )
wwvb/testcli.py CHANGED
@@ -6,50 +6,57 @@
6
6
  #
7
7
  # SPDX-License-Identifier: GPL-3.0-only
8
8
 
9
- # pylint: disable=invalid-name
10
-
9
+ import json
11
10
  import os
12
11
  import subprocess
13
12
  import sys
14
13
  import unittest
14
+ from typing import Any, Sequence
15
15
 
16
- coverage_add = (
17
- ("-m", "coverage", "run", "--branch", "-p") if "COVERAGE_RUN" in os.environ else ()
18
- )
16
+ coverage_add = ("-m", "coverage", "run", "--branch", "-p") if "COVERAGE_RUN" in os.environ else ()
19
17
 
20
18
 
21
19
  class CLITestCase(unittest.TestCase):
22
20
  """Test various CLI commands within wwvbpy"""
23
21
 
24
- def assertProgramOutput(self, expected: str, *args: str) -> None:
25
- """Check the output from invoking a program matches the expected"""
22
+ def programOutput(self, *args: str) -> str:
26
23
  env = os.environ.copy()
27
24
  env["PYTHONIOENCODING"] = "utf-8"
28
- actual = subprocess.check_output(
29
- args, stdin=subprocess.DEVNULL, encoding="utf-8", env=env
30
- )
25
+ return subprocess.check_output(args, stdin=subprocess.DEVNULL, encoding="utf-8", env=env)
26
+
27
+ def moduleArgs(self, *args: str) -> Sequence[str]:
28
+ return tuple((sys.executable, *coverage_add, "-m", *args))
29
+
30
+ def moduleOutput(self, *args: str) -> str:
31
+ return self.programOutput(sys.executable, *coverage_add, "-m", *args)
32
+
33
+ def assertProgramOutput(self, expected: str, *args: str) -> None:
34
+ """Check the output from invoking a program matches the expected"""
35
+ actual = self.programOutput(*args)
31
36
  self.assertMultiLineEqual(expected, actual, f"args={args}")
32
37
 
33
38
  def assertProgramOutputStarts(self, expected: str, *args: str) -> None:
34
39
  """Check the output from invoking a program matches the expected"""
35
- env = os.environ.copy()
36
- env["PYTHONIOENCODING"] = "utf-8"
37
- actual = subprocess.check_output(
38
- args, stdin=subprocess.DEVNULL, encoding="utf-8", env=env
39
- )
40
+ actual = self.programOutput(*args)
40
41
  self.assertMultiLineEqual(expected, actual[: len(expected)], f"args={args}")
41
42
 
42
43
  def assertModuleOutput(self, expected: str, *args: str) -> None:
43
44
  """Check the output from invoking a `python -m modulename` program matches the expected"""
44
- return self.assertProgramOutput(
45
- expected, sys.executable, *coverage_add, "-m", *args
46
- )
45
+ actual = self.moduleOutput(*args)
46
+ self.assertMultiLineEqual(expected, actual, f"args={args}")
47
+
48
+ def assertStarts(self, expected: str, actual: str, *args: str) -> None:
49
+ self.assertMultiLineEqual(expected, actual[: len(expected)], f"args={args}")
50
+
51
+ def assertModuleJson(self, expected: Any, *args: str) -> None:
52
+ """Check the output from invoking a `python -m modulename` program matches the expected"""
53
+ actual = self.moduleOutput(*args)
54
+ self.assertEqual(json.loads(actual), expected)
47
55
 
48
56
  def assertModuleOutputStarts(self, expected: str, *args: str) -> None:
49
57
  """Check the output from invoking a `python -m modulename` program matches the expected"""
50
- return self.assertProgramOutputStarts(
51
- expected, sys.executable, *coverage_add, "-m", *args
52
- )
58
+ actual = self.moduleOutput(*args)
59
+ self.assertStarts(expected, actual, *args)
53
60
 
54
61
  def assertProgramError(self, *args: str) -> None:
55
62
  """Check the output from invoking a program fails"""
@@ -57,16 +64,12 @@ class CLITestCase(unittest.TestCase):
57
64
  env["PYTHONIOENCODING"] = "utf-8"
58
65
  with self.assertRaises(subprocess.SubprocessError):
59
66
  subprocess.check_output(
60
- args,
61
- stdin=subprocess.DEVNULL,
62
- stderr=subprocess.DEVNULL,
63
- encoding="utf-8",
64
- env=env,
67
+ args, stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL, encoding="utf-8", env=env
65
68
  )
66
69
 
67
70
  def assertModuleError(self, *args: str) -> None:
68
71
  """Check the output from invoking a `python -m modulename` program fails"""
69
- return self.assertProgramError(sys.executable, *coverage_add, "-m", *args)
72
+ self.assertProgramError(*self.moduleArgs(*args))
70
73
 
71
74
  def test_gen(self) -> None:
72
75
  """test wwvb.gen"""
@@ -153,10 +156,25 @@ WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-300 ly=1 ls=0
153
156
 
154
157
  def test_json(self) -> None:
155
158
  """Test the JSON output format"""
156
- self.assertModuleOutput(
157
- """\
158
- [{"year": 2021, "days": 340, "hour": 3, "minute": 40, "amplitude": "210000000200000001120011001002000000010200010001020001000002", "phase": "111110011011010101000100100110011110001110111010111101001011"}, {"year": 2021, "days": 340, "hour": 3, "minute": 41, "amplitude": "210000001200000001120011001002000000010200010001020001000002", "phase": "001010011100100011000101110000100001101000001111101100000010"}]
159
- """,
159
+ self.assertModuleJson(
160
+ [
161
+ {
162
+ "year": 2021,
163
+ "days": 340,
164
+ "hour": 3,
165
+ "minute": 40,
166
+ "amplitude": "210000000200000001120011001002000000010200010001020001000002",
167
+ "phase": "111110011011010101000100100110011110001110111010111101001011",
168
+ },
169
+ {
170
+ "year": 2021,
171
+ "days": 340,
172
+ "hour": 3,
173
+ "minute": 41,
174
+ "amplitude": "210000001200000001120011001002000000010200010001020001000002",
175
+ "phase": "001010011100100011000101110000100001101000001111101100000010",
176
+ },
177
+ ],
160
178
  "wwvb.gen",
161
179
  "-m",
162
180
  "2",
@@ -166,10 +184,23 @@ WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-300 ly=1 ls=0
166
184
  "both",
167
185
  "2021-12-6 3:40",
168
186
  )
169
- self.assertModuleOutput(
170
- """\
171
- [{"year": 2021, "days": 340, "hour": 3, "minute": 40, "amplitude": "210000000200000001120011001002000000010200010001020001000002"}, {"year": 2021, "days": 340, "hour": 3, "minute": 41, "amplitude": "210000001200000001120011001002000000010200010001020001000002"}]
172
- """,
187
+ self.assertModuleJson(
188
+ [
189
+ {
190
+ "year": 2021,
191
+ "days": 340,
192
+ "hour": 3,
193
+ "minute": 40,
194
+ "amplitude": "210000000200000001120011001002000000010200010001020001000002",
195
+ },
196
+ {
197
+ "year": 2021,
198
+ "days": 340,
199
+ "hour": 3,
200
+ "minute": 41,
201
+ "amplitude": "210000001200000001120011001002000000010200010001020001000002",
202
+ },
203
+ ],
173
204
  "wwvb.gen",
174
205
  "-m",
175
206
  "2",
@@ -179,10 +210,23 @@ WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-300 ly=1 ls=0
179
210
  "amplitude",
180
211
  "2021-12-6 3:40",
181
212
  )
182
- self.assertModuleOutput(
183
- """\
184
- [{"year": 2021, "days": 340, "hour": 3, "minute": 40, "phase": "111110011011010101000100100110011110001110111010111101001011"}, {"year": 2021, "days": 340, "hour": 3, "minute": 41, "phase": "001010011100100011000101110000100001101000001111101100000010"}]
185
- """,
213
+ self.assertModuleJson(
214
+ [
215
+ {
216
+ "year": 2021,
217
+ "days": 340,
218
+ "hour": 3,
219
+ "minute": 40,
220
+ "phase": "111110011011010101000100100110011110001110111010111101001011",
221
+ },
222
+ {
223
+ "year": 2021,
224
+ "days": 340,
225
+ "hour": 3,
226
+ "minute": 41,
227
+ "phase": "001010011100100011000101110000100001101000001111101100000010",
228
+ },
229
+ ],
186
230
  "wwvb.gen",
187
231
  "-m",
188
232
  "2",
@@ -198,9 +242,11 @@ WWVB timecode: year=2020 days=001 hour=12 min=30 dst=0 ut1=-300 ly=1 ls=0
198
242
  self.assertModuleOutput(
199
243
  """\
200
244
  WWVB timecode: year=2021 days=340 hour=03 min=40 dst=0 ut1=-100 ly=0 ls=0 --style=sextant
201
- 2021-340 03:40 🬋🬩🬋🬹🬩🬹🬩🬹🬩🬹🬍🬎🬍🬎🬩🬹🬩🬹🬋🬍🬩🬹🬩🬹🬍🬎🬩🬹🬍🬎🬩🬹🬍🬎🬋🬹🬋🬎🬋🬍🬍🬎🬩🬹🬋🬎🬋🬎🬩🬹🬍🬎🬋🬎🬩🬹🬩🬹🬋🬍🬍🬎🬩🬹🬩🬹🬩🬹🬩🬹🬍🬎🬍🬎🬋🬎🬩🬹🬋🬩🬩🬹🬍🬎🬩🬹🬋🬹🬩🬹🬍🬎🬩🬹🬋🬎🬩🬹🬋🬩🬩🬹🬩🬹🬍🬎🬋🬹🬍🬎🬍🬎🬩🬹🬍🬎🬩🬹🬋🬩
245
+ 2021-340 03:40 \
246
+ 🬋🬩🬋🬹🬩🬹🬩🬹🬩🬹🬍🬎🬍🬎🬩🬹🬩🬹🬋🬍🬩🬹🬩🬹🬍🬎🬩🬹🬍🬎🬩🬹🬍🬎🬋🬹🬋🬎🬋🬍🬍🬎🬩🬹🬋🬎🬋🬎🬩🬹🬍🬎🬋🬎🬩🬹🬩🬹🬋🬍🬍🬎🬩🬹🬩🬹🬩🬹🬩🬹🬍🬎🬍🬎🬋🬎🬩🬹🬋🬩🬩🬹🬍🬎🬩🬹🬋🬹🬩🬹🬍🬎🬩🬹🬋🬎🬩🬹🬋🬩🬩🬹🬩🬹🬍🬎🬋🬹🬍🬎🬍🬎🬩🬹🬍🬎🬩🬹🬋🬩
202
247
 
203
- 2021-340 03:41 🬋🬍🬋🬎🬩🬹🬍🬎🬩🬹🬍🬎🬍🬎🬩🬹🬋🬹🬋🬩🬍🬎🬍🬎🬩🬹🬍🬎🬍🬎🬍🬎🬩🬹🬋🬹🬋🬎🬋🬍🬍🬎🬩🬹🬋🬎🬋🬹🬩🬹🬩🬹🬋🬎🬍🬎🬍🬎🬋🬍🬩🬹🬍🬎🬍🬎🬍🬎🬍🬎🬩🬹🬩🬹🬋🬎🬩🬹🬋🬍🬍🬎🬍🬎🬍🬎🬋🬎🬩🬹🬩🬹🬩🬹🬋🬹🬩🬹🬋🬍🬩🬹🬩🬹🬍🬎🬋🬎🬍🬎🬍🬎🬍🬎🬍🬎🬩🬹🬋🬍
248
+ 2021-340 03:41 \
249
+ 🬋🬍🬋🬎🬩🬹🬍🬎🬩🬹🬍🬎🬍🬎🬩🬹🬋🬹🬋🬩🬍🬎🬍🬎🬩🬹🬍🬎🬍🬎🬍🬎🬩🬹🬋🬹🬋🬎🬋🬍🬍🬎🬩🬹🬋🬎🬋🬹🬩🬹🬩🬹🬋🬎🬍🬎🬍🬎🬋🬍🬩🬹🬍🬎🬍🬎🬍🬎🬍🬎🬩🬹🬩🬹🬋🬎🬩🬹🬋🬍🬍🬎🬍🬎🬍🬎🬋🬎🬩🬹🬩🬹🬩🬹🬋🬹🬩🬹🬋🬍🬩🬹🬩🬹🬍🬎🬋🬎🬍🬎🬍🬎🬍🬎🬍🬎🬩🬹🬋🬍
204
250
 
205
251
  """,
206
252
  "wwvb.gen",
wwvb/testdaylight.py CHANGED
@@ -19,9 +19,7 @@ class TestDaylight(unittest.TestCase):
19
19
  """Test that the onset of DST is the same in Mountain and WWVBMinute (which uses ls bits)"""
20
20
  for h in [8, 9, 10]:
21
21
  for dm in range(-1441, 1442):
22
- d = datetime.datetime(
23
- 2021, 3, 14, h, 0, tzinfo=datetime.timezone.utc
24
- ) + datetime.timedelta(minutes=dm)
22
+ d = datetime.datetime(2021, 3, 14, h, 0, tzinfo=datetime.timezone.utc) + datetime.timedelta(minutes=dm)
25
23
  m = wwvb.WWVBMinute.from_datetime(d)
26
24
  self.assertEqual(
27
25
  m.as_datetime_local().replace(tzinfo=Mountain),
@@ -32,9 +30,7 @@ class TestDaylight(unittest.TestCase):
32
30
  """Test that the end of DST is the same in Mountain and WWVBMinute (which uses ls bits)"""
33
31
  for h in [7, 8, 9]:
34
32
  for dm in range(-1441, 1442):
35
- d = datetime.datetime(
36
- 2021, 11, 7, h, 0, tzinfo=datetime.timezone.utc
37
- ) + datetime.timedelta(minutes=dm)
33
+ d = datetime.datetime(2021, 11, 7, h, 0, tzinfo=datetime.timezone.utc) + datetime.timedelta(minutes=dm)
38
34
  m = wwvb.WWVBMinute.from_datetime(d)
39
35
  self.assertEqual(
40
36
  m.as_datetime_local().replace(tzinfo=Mountain),
@@ -45,9 +41,7 @@ class TestDaylight(unittest.TestCase):
45
41
  """Test that middle of DST is the same in Mountain and WWVBMinute (which uses ls bits)"""
46
42
  for h in [7, 8, 9]:
47
43
  for dm in (-1, 0, 1):
48
- d = datetime.datetime(
49
- 2021, 7, 7, h, 0, tzinfo=datetime.timezone.utc
50
- ) + datetime.timedelta(minutes=dm)
44
+ d = datetime.datetime(2021, 7, 7, h, 0, tzinfo=datetime.timezone.utc) + datetime.timedelta(minutes=dm)
51
45
  m = wwvb.WWVBMinute.from_datetime(d)
52
46
  self.assertEqual(
53
47
  m.as_datetime_local().replace(tzinfo=Mountain),
@@ -58,9 +52,7 @@ class TestDaylight(unittest.TestCase):
58
52
  """Test that middle of standard time is the same in Mountain and WWVBMinute (which uses ls bits)"""
59
53
  for h in [7, 8, 9]:
60
54
  for dm in (-1, 0, 1):
61
- d = datetime.datetime(
62
- 2021, 12, 25, h, 0, tzinfo=datetime.timezone.utc
63
- ) + datetime.timedelta(minutes=dm)
55
+ d = datetime.datetime(2021, 12, 25, h, 0, tzinfo=datetime.timezone.utc) + datetime.timedelta(minutes=dm)
64
56
  m = wwvb.WWVBMinute.from_datetime(d)
65
57
  self.assertEqual(
66
58
  m.as_datetime_local().replace(tzinfo=Mountain),
wwvb/testls.py CHANGED
@@ -55,9 +55,7 @@ class TestLeapSecond(unittest.TestCase):
55
55
  leap.append(nm)
56
56
  else:
57
57
  assert not our_is_ls
58
- d = datetime.datetime.combine(nm, datetime.time()).replace(
59
- tzinfo=datetime.timezone.utc
60
- )
58
+ d = datetime.datetime.combine(nm, datetime.time()).replace(tzinfo=datetime.timezone.utc)
61
59
  self.assertEqual(leap, bench)
62
60
 
63
61
 
wwvb/testpm.py CHANGED
@@ -15,23 +15,9 @@ class TestPhaseModulation(unittest.TestCase):
15
15
 
16
16
  def test_pm(self) -> None:
17
17
  """Compare the generated signal from a reference minute in NIST docs"""
18
- ref_am = (
19
- "2011000002"
20
- "0001001112"
21
- "0001010002"
22
- "0110001012"
23
- "0100000012"
24
- "0010010112"
25
- )
26
-
27
- ref_pm = (
28
- "0011101101"
29
- "0001001000"
30
- "0011001000"
31
- "0110001101"
32
- "0011010001"
33
- "0110110110"
34
- )
18
+ ref_am = "2011000002" "0001001112" "0001010002" "0110001012" "0100000012" "0010010112"
19
+
20
+ ref_pm = "0011101101" "0001001000" "0011001000" "0110001101" "0011010001" "0110110110"
35
21
 
36
22
  ref_minute = wwvb.WWVBMinuteIERS(2012, 186, 17, 30, dst=3)
37
23
  ref_time = ref_minute.as_timecode()
wwvb/testuwwvb.py CHANGED
@@ -21,10 +21,10 @@ EitherDatetimeOrNone = Union[None, datetime.datetime, adafruit_datetime.datetime
21
21
  class WWVBRoundtrip(unittest.TestCase):
22
22
  """tests of uwwvb.py"""
23
23
 
24
- def assertDateTimeEqualExceptTzInfo( # pylint: disable=invalid-name
25
- self, a: EitherDatetimeOrNone, b: EitherDatetimeOrNone
26
- ) -> None:
27
- """Test two datetime objects for equality, excluding tzinfo, and allowing adafruit_datetime and core datetime modules to compare equal"""
24
+ def assertDateTimeEqualExceptTzInfo(self, a: EitherDatetimeOrNone, b: EitherDatetimeOrNone) -> None:
25
+ """Test two datetime objects for equality
26
+
27
+ This equality test excludes tzinfo, and allows adafruit_datetime and core datetime modules to compare equal"""
28
28
  assert a
29
29
  assert b
30
30
  self.assertEqual(
@@ -35,9 +35,7 @@ class WWVBRoundtrip(unittest.TestCase):
35
35
  def test_decode(self) -> None:
36
36
  """Test decoding of some minutes including a leap second.
37
37
  Each minute must decode and match the primary decoder."""
38
- minute = wwvb.WWVBMinuteIERS.from_datetime(
39
- datetime.datetime(2012, 6, 30, 23, 50)
40
- )
38
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
41
39
  assert minute
42
40
  decoder = uwwvb.WWVBDecoder()
43
41
  decoder.update(uwwvb.MARK)
@@ -60,17 +58,13 @@ class WWVBRoundtrip(unittest.TestCase):
60
58
  def test_roundtrip(self) -> None:
61
59
  """Test that some big range of times all decode the same as the primary decoder"""
62
60
  dt = datetime.datetime(2002, 1, 1, 0, 0)
63
- delta = datetime.timedelta(
64
- minutes=7182 if sys.implementation.name == "cpython" else 86400 - 7182
65
- )
61
+ delta = datetime.timedelta(minutes=7182 if sys.implementation.name == "cpython" else 86400 - 7182)
66
62
  while dt.year < 2013:
67
63
  minute = wwvb.WWVBMinuteIERS.from_datetime(dt)
68
64
  assert minute
69
65
  decoded = uwwvb.decode_wwvb([int(i) for i in minute.as_timecode().am])
70
66
  assert decoded
71
- self.assertDateTimeEqualExceptTzInfo(
72
- minute.as_datetime_utc(), uwwvb.as_datetime_utc(decoded)
73
- )
67
+ self.assertDateTimeEqualExceptTzInfo(minute.as_datetime_utc(), uwwvb.as_datetime_utc(decoded))
74
68
  dt = dt + delta
75
69
 
76
70
  def test_dst(self) -> None:
@@ -93,9 +87,7 @@ class WWVBRoundtrip(unittest.TestCase):
93
87
  minute = wwvb.WWVBMinuteIERS.from_datetime(dt)
94
88
  decoded = uwwvb.decode_wwvb([int(i) for i in minute.as_timecode().am])
95
89
  assert decoded
96
- self.assertDateTimeEqualExceptTzInfo(
97
- minute.as_datetime_local(), uwwvb.as_datetime_local(decoded)
98
- )
90
+ self.assertDateTimeEqualExceptTzInfo(minute.as_datetime_local(), uwwvb.as_datetime_local(decoded))
99
91
 
100
92
  decoded = uwwvb.decode_wwvb([int(i) for i in minute.as_timecode().am])
101
93
  assert decoded
@@ -106,9 +98,7 @@ class WWVBRoundtrip(unittest.TestCase):
106
98
 
107
99
  def test_noise(self) -> None:
108
100
  """Test of the state-machine decoder when faced with pseudorandom noise"""
109
- minute = wwvb.WWVBMinuteIERS.from_datetime(
110
- datetime.datetime(2012, 6, 30, 23, 50)
111
- )
101
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
112
102
  r = random.Random(408)
113
103
  junk = [
114
104
  r.choice(
@@ -141,9 +131,7 @@ class WWVBRoundtrip(unittest.TestCase):
141
131
 
142
132
  def test_noise2(self) -> None:
143
133
  """Test of the full minute decoder with targeted errors to get full coverage"""
144
- minute = wwvb.WWVBMinuteIERS.from_datetime(
145
- datetime.datetime(2012, 6, 30, 23, 50)
146
- )
134
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
147
135
  timecode = minute.as_timecode()
148
136
  decoded = uwwvb.decode_wwvb([int(i) for i in timecode.am])
149
137
  self.assertIsNotNone(decoded)
@@ -178,9 +166,7 @@ class WWVBRoundtrip(unittest.TestCase):
178
166
 
179
167
  def test_noise3(self) -> None:
180
168
  """Test impossible BCD values"""
181
- minute = wwvb.WWVBMinuteIERS.from_datetime(
182
- datetime.datetime(2012, 6, 30, 23, 50)
183
- )
169
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
184
170
  timecode = minute.as_timecode()
185
171
 
186
172
  for poslist in [
wwvb/testwwvb.py CHANGED
@@ -27,7 +27,6 @@ class WWVBMinute2k(wwvb.WWVBMinute):
27
27
  epoch = 2000
28
28
 
29
29
 
30
- # pylint: disable=too-many-locals
31
30
  class WWVBTestCase(unittest.TestCase):
32
31
  """Test each expected output in tests/. Some outputs are from another program, some are from us"""
33
32
 
@@ -85,9 +84,7 @@ class WWVBRoundtrip(unittest.TestCase):
85
84
 
86
85
  def test_decode(self) -> None:
87
86
  """Test that a range of minutes including a leap second are correctly decoded by the state-based decoder"""
88
- minute = wwvb.WWVBMinuteIERS.from_datetime(
89
- datetime.datetime(1992, 6, 30, 23, 50)
90
- )
87
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50))
91
88
  decoder = decode.wwvbreceive()
92
89
  next(decoder)
93
90
  decoder.send(wwvb.AmplitudeModulation.MARK)
@@ -126,17 +123,13 @@ class WWVBRoundtrip(unittest.TestCase):
126
123
  def test_roundtrip(self) -> None:
127
124
  """Test that a wide of minutes are correctly decoded by the state-based decoder"""
128
125
  dt = datetime.datetime(1992, 1, 1, 0, 0)
129
- delta = datetime.timedelta(
130
- minutes=915 if sys.implementation.name == "cpython" else 86400 - 915
131
- )
126
+ delta = datetime.timedelta(minutes=915 if sys.implementation.name == "cpython" else 86400 - 915)
132
127
  while dt.year < 1993:
133
128
  minute = wwvb.WWVBMinuteIERS.from_datetime(dt)
134
129
  assert minute is not None
135
130
  timecode = minute.as_timecode().am
136
131
  assert timecode
137
- decoded_minute: Optional[
138
- wwvb.WWVBMinute
139
- ] = wwvb.WWVBMinuteIERS.from_timecode_am(minute.as_timecode())
132
+ decoded_minute: Optional[wwvb.WWVBMinute] = wwvb.WWVBMinuteIERS.from_timecode_am(minute.as_timecode())
140
133
  assert decoded_minute
141
134
  decoded = decoded_minute.as_timecode().am
142
135
  self.assertEqual(
@@ -148,9 +141,7 @@ class WWVBRoundtrip(unittest.TestCase):
148
141
 
149
142
  def test_noise(self) -> None:
150
143
  """Test against pseudorandom noise"""
151
- minute = wwvb.WWVBMinuteIERS.from_datetime(
152
- datetime.datetime(1992, 6, 30, 23, 50)
153
- )
144
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50))
154
145
  r = random.Random(408)
155
146
  junk = [
156
147
  r.choice(
@@ -180,9 +171,7 @@ class WWVBRoundtrip(unittest.TestCase):
180
171
 
181
172
  def test_noise2(self) -> None:
182
173
  """Test of the full minute decoder with targeted errors to get full coverage"""
183
- minute = wwvb.WWVBMinuteIERS.from_datetime(
184
- datetime.datetime(2012, 6, 30, 23, 50)
185
- )
174
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
186
175
  timecode = minute.as_timecode()
187
176
  decoded = wwvb.WWVBMinute.from_timecode_am(timecode)
188
177
  self.assertIsNotNone(decoded)
@@ -217,9 +206,7 @@ class WWVBRoundtrip(unittest.TestCase):
217
206
 
218
207
  def test_noise3(self) -> None:
219
208
  """Test impossible BCD values"""
220
- minute = wwvb.WWVBMinuteIERS.from_datetime(
221
- datetime.datetime(2012, 6, 30, 23, 50)
222
- )
209
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50))
223
210
  timecode = minute.as_timecode()
224
211
 
225
212
  for poslist in [
@@ -241,16 +228,12 @@ class WWVBRoundtrip(unittest.TestCase):
241
228
 
242
229
  def test_previous_next_minute(self) -> None:
243
230
  """Test that previous minute and next minute are inverses"""
244
- minute = wwvb.WWVBMinuteIERS.from_datetime(
245
- datetime.datetime(1992, 6, 30, 23, 50)
246
- )
231
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50))
247
232
  self.assertEqual(minute, minute.next_minute().previous_minute())
248
233
 
249
234
  def test_timecode_str(self) -> None:
250
235
  """Test the str() and repr() methods"""
251
- minute = wwvb.WWVBMinuteIERS.from_datetime(
252
- datetime.datetime(1992, 6, 30, 23, 50)
253
- )
236
+ minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(1992, 6, 30, 23, 50))
254
237
  timecode = minute.as_timecode()
255
238
  self.assertEqual(
256
239
  str(timecode),
@@ -268,9 +251,7 @@ class WWVBRoundtrip(unittest.TestCase):
268
251
  sm1 = s - datetime.timedelta(days=1)
269
252
  self.assertEqual(wwvb.get_dut1(s), wwvb.get_dut1(sm1))
270
253
 
271
- e = iersdata.DUT1_DATA_START + datetime.timedelta(
272
- days=len(iersdata.DUT1_OFFSETS) - 1
273
- )
254
+ e = iersdata.DUT1_DATA_START + datetime.timedelta(days=len(iersdata.DUT1_OFFSETS) - 1)
274
255
  ep1 = e + datetime.timedelta(days=1)
275
256
 
276
257
  self.assertEqual(wwvb.get_dut1(e), wwvb.get_dut1(ep1))
@@ -290,17 +271,11 @@ class WWVBRoundtrip(unittest.TestCase):
290
271
 
291
272
  s = "WWVB timecode: year=1998 days=365 hour=23 min=56 dst=0 ut1=-300 ly=0 ls=1"
292
273
  t = "year=1998 days=365 hour=23 min=56 dst=0 ut1=-300 ly=0 ls=1"
293
- self.assertEqual(
294
- wwvb.WWVBMinuteIERS.fromstring(s), wwvb.WWVBMinuteIERS.fromstring(t)
295
- )
274
+ self.assertEqual(wwvb.WWVBMinuteIERS.fromstring(s), wwvb.WWVBMinuteIERS.fromstring(t))
296
275
  t = "year=1998 days=365 hour=23 min=56 dst=0 ut1=-300 ls=1"
297
- self.assertEqual(
298
- wwvb.WWVBMinuteIERS.fromstring(s), wwvb.WWVBMinuteIERS.fromstring(t)
299
- )
276
+ self.assertEqual(wwvb.WWVBMinuteIERS.fromstring(s), wwvb.WWVBMinuteIERS.fromstring(t))
300
277
  t = "year=1998 days=365 hour=23 min=56 dst=0"
301
- self.assertEqual(
302
- wwvb.WWVBMinuteIERS.fromstring(s), wwvb.WWVBMinuteIERS.fromstring(t)
303
- )
278
+ self.assertEqual(wwvb.WWVBMinuteIERS.fromstring(s), wwvb.WWVBMinuteIERS.fromstring(t))
304
279
 
305
280
  def test_from_datetime(self) -> None:
306
281
  """Test the from_datetime() classmethod"""
@@ -322,9 +297,7 @@ class WWVBRoundtrip(unittest.TestCase):
322
297
  wwvb.WWVBMinute(2021, 1, 1, 1, ls=False)
323
298
 
324
299
  with self.assertRaises(ValueError):
325
- wwvb.WWVBMinute.fromstring(
326
- "year=1998 days=365 hour=23 min=56 dst=0 ut1=-300 ly=0 ls=1 boo=1"
327
- )
300
+ wwvb.WWVBMinute.fromstring("year=1998 days=365 hour=23 min=56 dst=0 ut1=-300 ly=0 ls=1 boo=1")
328
301
 
329
302
  def test_deprecated(self) -> None:
330
303
  """Ensure that the 'maybe_warn_update' function is covered"""
@@ -357,25 +330,19 @@ class WWVBRoundtrip(unittest.TestCase):
357
330
  wwvb.get_dst_next(datetime.datetime(2005, 1, 1), tz=tz.ZoneInfo("Cuba")),
358
331
  0b101111,
359
332
  )
360
- date, row = wwvb.get_dst_change_date_and_row(
361
- datetime.datetime(2005, 1, 1), tz=tz.ZoneInfo("Cuba")
362
- )
333
+ date, row = wwvb.get_dst_change_date_and_row(datetime.datetime(2005, 1, 1), tz=tz.ZoneInfo("Cuba"))
363
334
  self.assertIsNone(date)
364
335
  self.assertIsNone(row)
365
336
 
366
337
  # California was weird in 1948
367
338
  self.assertEqual(
368
- wwvb.get_dst_next(
369
- datetime.datetime(1948, 1, 1), tz=tz.ZoneInfo("America/Los_Angeles")
370
- ),
339
+ wwvb.get_dst_next(datetime.datetime(1948, 1, 1), tz=tz.ZoneInfo("America/Los_Angeles")),
371
340
  0b100011,
372
341
  )
373
342
 
374
343
  # Berlin had DST changes on Monday in 1917
375
344
  self.assertEqual(
376
- wwvb.get_dst_next(
377
- datetime.datetime(1917, 1, 1), tz=tz.ZoneInfo("Europe/Berlin")
378
- ),
345
+ wwvb.get_dst_next(datetime.datetime(1917, 1, 1), tz=tz.ZoneInfo("Europe/Berlin")),
379
346
  0b100011,
380
347
  )
381
348
 
@@ -383,9 +350,7 @@ class WWVBRoundtrip(unittest.TestCase):
383
350
  # Australia observes DST in the other half of the year compared to the
384
351
  # Northern hemisphere
385
352
  self.assertEqual(
386
- wwvb.get_dst_next(
387
- datetime.datetime(2005, 1, 1), tz=tz.ZoneInfo("Australia/Melbourne")
388
- ),
353
+ wwvb.get_dst_next(datetime.datetime(2005, 1, 1), tz=tz.ZoneInfo("Australia/Melbourne")),
389
354
  0b100011,
390
355
  )
391
356
 
wwvb/updateiers.py CHANGED
@@ -23,15 +23,12 @@ DIST_PATH = str(pathlib.Path(__file__).parent / "iersdata_dist.py")
23
23
 
24
24
  OLD_TABLE_START: Optional[datetime.date] = None
25
25
  OLD_TABLE_END: Optional[datetime.date] = None
26
- try:
26
+ if os.path.exists(DIST_PATH):
27
27
  import wwvb.iersdata_dist
28
28
 
29
29
  OLD_TABLE_START = wwvb.iersdata_dist.DUT1_DATA_START
30
- OLD_TABLE_END = OLD_TABLE_START + datetime.timedelta(
31
- days=len(wwvb.iersdata_dist.DUT1_OFFSETS) - 1
32
- )
33
- except (ImportError, NameError) as e:
34
- pass
30
+ OLD_TABLE_END = OLD_TABLE_START + datetime.timedelta(days=len(wwvb.iersdata_dist.DUT1_OFFSETS) - 1)
31
+
35
32
  IERS_URL = "https://datacenter.iers.org/data/csv/finals2000A.all.csv"
36
33
  if os.path.exists("finals2000A.all.csv"):
37
34
  IERS_URL = "finals2000A.all.csv"
@@ -48,7 +45,7 @@ def _get_text(url: str) -> str:
48
45
  return open(url, encoding="utf-8").read()
49
46
 
50
47
 
51
- def update_iersdata( # pylint: disable=too-many-locals, too-many-branches, too-many-statements
48
+ def update_iersdata(
52
49
  target_file: str,
53
50
  ) -> None:
54
51
  """Update iersdata.py"""
@@ -93,11 +90,7 @@ def update_iersdata( # pylint: disable=too-many-locals, too-many-branches, too-
93
90
  assert wwvb_dut1_table
94
91
  meta = wwvb_data.find("meta", property="article:modified_time")
95
92
  assert isinstance(meta, bs4.Tag)
96
- wwvb_data_stamp = (
97
- datetime.datetime.fromisoformat(meta.attrs["content"])
98
- .replace(tzinfo=None)
99
- .date()
100
- )
93
+ wwvb_data_stamp = datetime.datetime.fromisoformat(meta.attrs["content"]).replace(tzinfo=None).date()
101
94
 
102
95
  def patch(patch_start: datetime.date, patch_end: datetime.date, val: int) -> None:
103
96
  off_start = (patch_start - table_start).days
@@ -137,21 +130,18 @@ def update_iersdata( # pylint: disable=too-many-locals, too-many-branches, too-
137
130
  print(*args, file=output)
138
131
 
139
132
  code("# -*- python3 -*-")
133
+ code("# fmt: off")
140
134
  code('"""File generated from public data - not subject to copyright"""')
141
135
  code("# SPDX" + "-FileCopyrightText: Public domain")
142
136
  code("# SPDX" + "-License-Identifier: CC0-1.0")
143
- code("# fmt: off")
144
137
  code("# isort: skip_file")
145
- code("# pylint: disable=invalid-name")
146
138
  code("import datetime")
147
139
 
148
140
  code("__all__ = ['DUT1_DATA_START', 'DUT1_OFFSETS']")
149
141
  code(f"DUT1_DATA_START = {repr(table_start)}")
150
142
  c = sorted(chr(ord("a") + ch + 10) for ch in set(offsets))
151
143
  code(f"{','.join(c)} = tuple({repr(''.join(c))})")
152
- code(
153
- f"DUT1_OFFSETS = str( # {table_start.year:04d}{table_start.month:02d}{table_start.day:02d}"
154
- )
144
+ code(f"DUT1_OFFSETS = str( # {table_start.year:04d}{table_start.month:02d}{table_start.day:02d}")
155
145
  line = ""
156
146
  j = 0
157
147
 
@@ -194,9 +184,7 @@ def iersdata_path(callback: Callable[[str, str], str]) -> str:
194
184
  default=iersdata_path(platformdirs.user_data_dir),
195
185
  )
196
186
  @click.option("--dist", "location", flag_value=DIST_PATH)
197
- @click.option(
198
- "--site", "location", flag_value=iersdata_path(platformdirs.site_data_dir)
199
- )
187
+ @click.option("--site", "location", flag_value=iersdata_path(platformdirs.site_data_dir))
200
188
  def main(location: str) -> None:
201
189
  """Update DUT1 data"""
202
190
  print("will write to", location)
wwvb/wwvbtk.py CHANGED
@@ -9,7 +9,7 @@
9
9
  import functools
10
10
  import threading
11
11
  import time
12
- from tkinter import Canvas, TclError, Tk # pylint: disable=import-error
12
+ from tkinter import Canvas, TclError, Tk
13
13
  from typing import Any, Generator, Optional, Tuple
14
14
 
15
15
  import click
@@ -23,9 +23,7 @@ def _app() -> Tk:
23
23
  return Tk()
24
24
 
25
25
 
26
- def validate_colors( # pylint: disable=unused-argument
27
- ctx: Any, param: Any, value: str
28
- ) -> list[str]:
26
+ def validate_colors(ctx: Any, param: Any, value: str) -> list[str]:
29
27
  """Check that all colors in a string are valid, splitting it to a list"""
30
28
  app = _app()
31
29
  colors = value.split()
@@ -79,9 +77,7 @@ def main(colors: list[str], size: int, min_size: Optional[int]) -> None:
79
77
  yield timestamp + i, code
80
78
  timestamp = timestamp + 60
81
79
 
82
- def wwvbsmarttick() -> (
83
- Generator[Tuple[float, wwvb.AmplitudeModulation], None, None]
84
- ):
80
+ def wwvbsmarttick() -> Generator[Tuple[float, wwvb.AmplitudeModulation], None, None]:
85
81
  """Yield consecutive values of the WWVB amplitude signal but deal with time
86
82
  progressing unexpectedly, such as when the computer is suspended or NTP steps
87
83
  the clock backwards
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wwvb
3
- Version: 3.0.6
3
+ Version: 3.0.8
4
4
  Summary: Generate WWVB timecodes for any desired time
5
5
  Home-page: https://github.com/jepler/wwvbpy
6
6
  Author: Jeff Epler
@@ -0,0 +1,23 @@
1
+ uwwvb.py,sha256=UX5oSMs4KZPt1hg1urZXhN0pMU3BvQWYOKExZT8FiIo,5674
2
+ wwvb/__init__.py,sha256=oUUcweJnrFDVw5_-smJopfVAk5yNFlEnOkYMixNxZzc,29762
3
+ wwvb/__version__.py,sha256=UsWpfolO3V5lhkRSzLYHaglRPoxe1Nfcc4kMi3j-8W8,411
4
+ wwvb/decode.py,sha256=PBfBzYJJkIh8VD-d_SHvBl3gX4FpXhqG1lH2fP8lUaI,2758
5
+ wwvb/dut1table.py,sha256=uqaCnCOWr7ytx-nt3mmyhFb9jJVNP5N-WJX-glunKAk,890
6
+ wwvb/gen.py,sha256=pOSgkJTh_TDKN1FulzbAW8aN77bJXtJjo1KGoBEU3Dk,3697
7
+ wwvb/iersdata.py,sha256=hJFj-ZL3RP13dcdFGWoT6WOgoV-6CAy-sWSYfi_EXwg,965
8
+ wwvb/iersdata_dist.py,sha256=dmd6ShKPG5AHBm0dfRAKFpFpt8WDiW1NqNF8DLeMgrI,2363
9
+ wwvb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ wwvb/testcli.py,sha256=06uIEdg-ZH0UOISr0X7oy4iAQ5eb4-MejQC7TeqIJz8,10086
11
+ wwvb/testdaylight.py,sha256=JW8UJK-FeAg9Kjy5F_aBYbUVj44DKpJOXQ-u9ooyprA,2485
12
+ wwvb/testls.py,sha256=Kz4-MWLaUKABwyql8sWdzvtg8gipxhHv4r-6fn3fptg,1720
13
+ wwvb/testpm.py,sha256=I5ajzZjUypyKszSe4sTnMZbS43nPmee4H1G3uZlHC_w,935
14
+ wwvb/testuwwvb.py,sha256=oj68-Vv1EFBG9MOWB8zW2sTN1Yg22nVYOL_Aor3IJTI,8398
15
+ wwvb/testwwvb.py,sha256=bWMI0-Qbaye9Afil4qyPKsNYtY-B6d1wBJZUn-MS99w,16109
16
+ wwvb/tz.py,sha256=XVYh0btrnyP_nUiZUwBjufkYbb26_DTiZVl-R_1BA2A,299
17
+ wwvb/updateiers.py,sha256=zpMvIzuPvKdA-XvBcOwQiFx7e9yH-V-VypL95bG5rWk,7240
18
+ wwvb/wwvbtk.py,sha256=fFXoyReyGZIEpddz74KY0vKfLm1ZP1jwDHTTAbVd8XY,4525
19
+ wwvb-3.0.8.dist-info/METADATA,sha256=99ps6bN93tu3nAzKIVWepexqDDaPtfeg_SR4a0c2fRI,10290
20
+ wwvb-3.0.8.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
21
+ wwvb-3.0.8.dist-info/entry_points.txt,sha256=KSevvHWLEKxOxUQ-L-OQidD4Sj2BPEfhZ2TQhOgyys4,179
22
+ wwvb-3.0.8.dist-info/top_level.txt,sha256=0IYdkhEAMgurpv_F-76rlyn4GdxepGFzG99tivVdQVU,11
23
+ wwvb-3.0.8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.3)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,23 +0,0 @@
1
- uwwvb.py,sha256=Fiym40qquFafHSpOjetQqlYEJHN65dFOdRTVp3MLodA,5718
2
- wwvb/__init__.py,sha256=r5qzreH70tOD5N-FrR5aI5HTA3BoWuZWgHwi-sevHhM,30058
3
- wwvb/__version__.py,sha256=uNMH75I1pte97qzohjB8IClzvqSWDoMr4D9lZLC76js,411
4
- wwvb/decode.py,sha256=7x_3ThwcROZflmpCwW1AT1zAIM5Q1wzuhr2cEz_I--w,2851
5
- wwvb/dut1table.py,sha256=AG_pbfbaM1tOnudx0coFjLQ4JnoVnTCW6Ki0c-tH79Q,889
6
- wwvb/gen.py,sha256=RQOCk5gPuA01rpOkDO634Yae7LnMe69tqDvf3JQZvfQ,3850
7
- wwvb/iersdata.py,sha256=5MtuRHtmtd75Kht3j0io8A33gx-_ELew7fux1Mp1VPA,1000
8
- wwvb/iersdata_dist.py,sha256=ViVd6QR4_ktWax5dHLdu-c69bIrBruWY_AYt7WBYdgs,2394
9
- wwvb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- wwvb/testcli.py,sha256=O-B3wchI11MpiAvLr-nBQ-HMCR0OIauWsKZWSjB6zcg,8751
11
- wwvb/testdaylight.py,sha256=jr9i2SmIn49x4tfqKJtUoQzNV_jgtjbHJu3B_wzBeNU,2637
12
- wwvb/testls.py,sha256=9E94nEqvTI2yyp9ssYrTfjzZ2vxsOud9sGT_Ba4CJ18,1750
13
- wwvb/testpm.py,sha256=10_oPNLn5mF5tVXbZ5uruZpNiOIJcyIWCWLKzv-P5oo,1103
14
- wwvb/testuwwvb.py,sha256=87xDJH-GWFip6SN5CEhmRS0UrA3YFw0KG6pO3iVZIZs,8590
15
- wwvb/testwwvb.py,sha256=bJH8BHT8WA35ys--tM63wL3rdPyOfLfcRWK0uVhyxko,16557
16
- wwvb/tz.py,sha256=XVYh0btrnyP_nUiZUwBjufkYbb26_DTiZVl-R_1BA2A,299
17
- wwvb/updateiers.py,sha256=T5tiwnEBAJgAVmjMBi8OCf0zgjPdasN8SmJxa6oDrmY,7459
18
- wwvb/wwvbtk.py,sha256=sP34Ebb0_3alri0GCorK5o0IBtFTQcIZi3lh9MgOiyo,4614
19
- wwvb-3.0.6.dist-info/METADATA,sha256=l41waOVz7AOMTr3PUBNe7sFwM8IS_tDSbYd-WROw1XU,10290
20
- wwvb-3.0.6.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
21
- wwvb-3.0.6.dist-info/entry_points.txt,sha256=KSevvHWLEKxOxUQ-L-OQidD4Sj2BPEfhZ2TQhOgyys4,179
22
- wwvb-3.0.6.dist-info/top_level.txt,sha256=0IYdkhEAMgurpv_F-76rlyn4GdxepGFzG99tivVdQVU,11
23
- wwvb-3.0.6.dist-info/RECORD,,