5050import platform
5151import re
5252import shutil
53+ import struct
5354import sys
5455import threading
56+ import warnings
5557
5658from collections .abc import Generator , Iterable , Iterator
5759from typing import IO , Any , NamedTuple , Union
6062if os .name == 'posix' :
6163 import stat
6264 import fcntl
63- import struct
6465elif os .name == 'nt' :
6566 import ctypes
6667 import ctypes .wintypes
@@ -1424,7 +1425,9 @@ def query_device_capacity(device_fd: IO[Any]) -> Byte:
14241425 # conditions for some possible errors. Really only for cases
14251426 # where it would add value to override the default exception
14261427 # message string.
1427- buffer = fcntl .ioctl (device_fd .fileno (), request_code , b'\x00 ' * buffer_size )
1428+ buffer = fcntl .ioctl ( # pylint: disable=possibly-used-before-assignment
1429+ device_fd .fileno (), request_code , b'\x00 ' * buffer_size
1430+ )
14281431
14291432 # Unpack the raw result from the ioctl call into a familiar
14301433 # python data type according to the ``fmt`` rules.
@@ -1513,17 +1516,17 @@ def getsize(path: str, bestprefix: bool = True, system: int = NIST) -> Bitmath:
15131516 size_bytes = os .path .getsize (_path )
15141517 if bestprefix :
15151518 return Byte (size_bytes ).best_prefix (system = system )
1516- else :
1517- return Byte (size_bytes )
1519+ return Byte (size_bytes )
15181520
15191521
1520- def listdir (
1522+ def listdir ( # pylint: disable=too-many-arguments,too-many-positional-arguments
15211523 search_base : str ,
15221524 followlinks : bool = False ,
1523- filter : str = '*' ,
1525+ glob : str = '*' ,
15241526 relpath : bool = False ,
15251527 bestprefix : bool = False ,
15261528 system : int = NIST ,
1529+ ** kwargs ,
15271530) -> Iterator [tuple [str , Bitmath ]]:
15281531 """This is a generator which recurses the directory tree
15291532`search_base`, yielding 2-tuples of:
@@ -1533,7 +1536,7 @@ def listdir(
15331536
15341537 - `search_base` - The directory to begin walking down.
15351538 - `followlinks` - Whether or not to follow symbolic links to directories
1536- - `filter ` - A glob (see :py:mod:`fnmatch`) to filter results with
1539+ - `glob ` - A glob (see :py:mod:`fnmatch`) to filter results with
15371540 (default: ``*``, everything)
15381541 - `relpath` - ``True`` to return the relative path from `pwd` or
15391542 ``False`` (default) to return the fully qualified path
@@ -1547,8 +1550,18 @@ def listdir(
15471550.. note:: Symlinks to **files** are followed automatically
15481551
15491552 """
1550- for root , dirs , files in os .walk (search_base , followlinks = followlinks ):
1551- for name in fnmatch .filter (files , filter ):
1553+ if 'filter' in kwargs :
1554+ warnings .warn (
1555+ "The 'filter' parameter of listdir() is deprecated as of 2.0.0 and will be "
1556+ "removed in a future release. Use 'glob' instead." ,
1557+ DeprecationWarning ,
1558+ stacklevel = 2 ,
1559+ )
1560+ glob = kwargs .pop ('filter' )
1561+ if kwargs :
1562+ raise TypeError (f"listdir() got unexpected keyword arguments: { list (kwargs )} " )
1563+ for root , _ , files in os .walk (search_base , followlinks = followlinks ):
1564+ for name in fnmatch .filter (files , glob ):
15521565 _path = os .path .join (root , name )
15531566 if relpath :
15541567 # RELATIVE path
@@ -1566,6 +1579,99 @@ def listdir(
15661579 yield (_return_path , getsize (_path , bestprefix = bestprefix , system = system ))
15671580
15681581
1582+ def _parse_string_strict (s : str ) -> 'Bitmath' :
1583+ if not isinstance (s , str ):
1584+ raise ValueError (f"parse_string only accepts string inputs but a { type (s )} was given" )
1585+
1586+ # get the index of the first alphabetic character
1587+ try :
1588+ index = next (i for i , c in enumerate (s ) if c .isalpha ())
1589+ except StopIteration :
1590+ raise ValueError (f"No unit detected, can not parse string '{ s } ' into a bitmath object" ) from None
1591+
1592+ # split the string into the value and the unit
1593+ val , unit = s [:index ], s [index :]
1594+
1595+ # see if the unit exists as a type in our namespace
1596+ if unit == "b" :
1597+ unit_class = Bit
1598+ elif unit == "B" :
1599+ unit_class = Byte
1600+ else :
1601+ if not (hasattr (sys .modules [__name__ ], unit ) and isinstance (getattr (sys .modules [__name__ ], unit ), type )):
1602+ raise ValueError (f"The unit { unit } is not a valid bitmath unit" )
1603+ unit_class = globals ()[unit ]
1604+
1605+ val = float (val )
1606+ return unit_class (val )
1607+
1608+
1609+ def _parse_string_unsafe (s : str | numbers .Number , system : int ) -> 'Bitmath' :
1610+ if not isinstance (s , str ) and not isinstance (s , numbers .Number ):
1611+ raise ValueError (f"parse_string only accepts string/number inputs but a { type (s )} was given" )
1612+
1613+ # Test case: raw number input (easy!)
1614+ if isinstance (s , numbers .Number ):
1615+ return Byte (s )
1616+
1617+ # Test case: a number pretending to be a string
1618+ if isinstance (s , str ):
1619+ try :
1620+ return Byte (float (s ))
1621+ except ValueError :
1622+ pass
1623+
1624+ # At this point the input is a string with a unit component.
1625+ # Separate the number and the unit.
1626+ try :
1627+ index = next (i for i , c in enumerate (s ) if c .isalpha ())
1628+ except StopIteration : # pragma: no cover
1629+ raise ValueError (f"No unit detected, can not parse string '{ s } ' into a bitmath object" ) from None
1630+
1631+ val , unit = s [:index ], s [index :]
1632+
1633+ # Explicit base-unit and word-form checks: handle B, b, bit(s),
1634+ # byte(s) before the prefix-normalization logic below.
1635+ _unit_lower = unit .lower ()
1636+ if unit == 'B' or _unit_lower in ('byte' , 'bytes' ):
1637+ return Byte (float (val ))
1638+ if unit == 'b' or _unit_lower in ('bit' , 'bits' ):
1639+ return Bit (float (val ))
1640+
1641+ # Normalise: strip trailing b/B and append 'B' so we always
1642+ # work with byte-family units regardless of what was supplied.
1643+ unit = unit .rstrip ('Bb' )
1644+ unit += 'B'
1645+
1646+ unit_class = None
1647+ if len (unit ) == 2 :
1648+ if system == NIST :
1649+ unit = capitalize_first (unit )
1650+ _unit = list (unit )
1651+ _unit .insert (1 , 'i' )
1652+ unit = '' .join (_unit )
1653+ if unit in globals ():
1654+ unit_class = globals ()[unit ]
1655+ else :
1656+ if unit .startswith ('K' ):
1657+ unit = unit .replace ('K' , 'k' )
1658+ elif not unit .startswith ('k' ):
1659+ unit = capitalize_first (unit )
1660+ if unit [0 ] in SI_PREFIXES :
1661+ unit_class = globals ()[unit ]
1662+ elif len (unit ) == 3 :
1663+ unit = capitalize_first (unit )
1664+ if unit [:2 ] in NIST_PREFIXES :
1665+ unit_class = globals ()[unit ]
1666+ else :
1667+ raise ValueError (f"The unit { unit } is not a valid bitmath unit" )
1668+
1669+ if unit_class is None :
1670+ raise ValueError (f"The unit { unit } is not a valid bitmath unit" )
1671+
1672+ return unit_class (float (val ))
1673+
1674+
15691675def parse_string (s : str | numbers .Number , system : int = NIST , strict : bool = True ) -> Bitmath :
15701676 """Parse a string with units and return a bitmath instance.
15711677
@@ -1606,102 +1712,8 @@ def parse_string(s: str | numbers.Number, system: int = NIST, strict: bool = Tru
16061712 defaults to ``bitmath.NIST`` and is ignored when ``strict=True``.
16071713 """
16081714 if strict :
1609- # Strings only please
1610- if not isinstance (s , str ):
1611- raise ValueError (f"parse_string only accepts string inputs but a { type (s )} was given" )
1612-
1613- # get the index of the first alphabetic character
1614- try :
1615- index = next (i for i , c in enumerate (s ) if c .isalpha ())
1616- except StopIteration :
1617- # If there's no alphabetic characters we won't be able to find a match
1618- raise ValueError (f"No unit detected, can not parse string '{ s } ' into a bitmath object" )
1619-
1620- # split the string into the value and the unit
1621- val , unit = s [:index ], s [index :]
1622-
1623- # see if the unit exists as a type in our namespace
1624- if unit == "b" :
1625- unit_class = Bit
1626- elif unit == "B" :
1627- unit_class = Byte
1628- else :
1629- if not (hasattr (sys .modules [__name__ ], unit ) and isinstance (getattr (sys .modules [__name__ ], unit ), type )):
1630- raise ValueError (f"The unit { unit } is not a valid bitmath unit" )
1631- unit_class = globals ()[unit ]
1632-
1633- try :
1634- val = float (val )
1635- except ValueError :
1636- raise
1637- return unit_class (val )
1638-
1639- else :
1640- # strict=False path (formerly parse_string_unsafe)
1641- if not isinstance (s , str ) and not isinstance (s , numbers .Number ):
1642- raise ValueError (f"parse_string only accepts string/number inputs but a { type (s )} was given" )
1643-
1644- # Test case: raw number input (easy!)
1645- if isinstance (s , numbers .Number ):
1646- return Byte (s )
1647-
1648- # Test case: a number pretending to be a string
1649- if isinstance (s , str ):
1650- try :
1651- return Byte (float (s ))
1652- except ValueError :
1653- pass
1654-
1655- # At this point the input is a string with a unit component.
1656- # Separate the number and the unit.
1657- try :
1658- index = next (i for i , c in enumerate (s ) if c .isalpha ())
1659- except StopIteration : # pragma: no cover
1660- raise ValueError (f"No unit detected, can not parse string '{ s } ' into a bitmath object" )
1661-
1662- val , unit = s [:index ], s [index :]
1663-
1664- # Explicit base-unit and word-form checks: handle B, b, bit(s),
1665- # byte(s) before the prefix-normalization logic below.
1666- _unit_lower = unit .lower ()
1667- if unit == 'B' or _unit_lower in ('byte' , 'bytes' ):
1668- return Byte (float (val ))
1669- if unit == 'b' or _unit_lower in ('bit' , 'bits' ):
1670- return Bit (float (val ))
1671-
1672- # Normalise: strip trailing b/B and append 'B' so we always
1673- # work with byte-family units regardless of what was supplied.
1674- unit = unit .rstrip ('Bb' )
1675- unit += 'B'
1676-
1677- if len (unit ) == 2 :
1678- if system == NIST :
1679- unit = capitalize_first (unit )
1680- _unit = list (unit )
1681- _unit .insert (1 , 'i' )
1682- unit = '' .join (_unit )
1683- if unit in globals ():
1684- unit_class = globals ()[unit ]
1685- else :
1686- if unit .startswith ('K' ):
1687- unit = unit .replace ('K' , 'k' )
1688- elif not unit .startswith ('k' ):
1689- unit = capitalize_first (unit )
1690- if unit [0 ] in SI_PREFIXES :
1691- unit_class = globals ()[unit ]
1692- elif len (unit ) == 3 :
1693- unit = capitalize_first (unit )
1694- if unit [:2 ] in NIST_PREFIXES :
1695- unit_class = globals ()[unit ]
1696- else :
1697- raise ValueError (f"The unit { unit } is not a valid bitmath unit" )
1698-
1699- try :
1700- unit_class
1701- except UnboundLocalError :
1702- raise ValueError (f"The unit { unit } is not a valid bitmath unit" )
1703-
1704- return unit_class (float (val ))
1715+ return _parse_string_strict (s )
1716+ return _parse_string_unsafe (s , system )
17051717
17061718
17071719def parse_string_unsafe (s : str | numbers .Number , system : int = NIST ) -> Bitmath :
@@ -1718,7 +1730,6 @@ def parse_string_unsafe(s: str | numbers.Number, system: int = NIST) -> Bitmath:
17181730 warnings.filterwarnings('ignore', category=DeprecationWarning,
17191731 module='bitmath')
17201732 """
1721- import warnings
17221733 warnings .warn (
17231734 "parse_string_unsafe is deprecated as of 2.0.0 and will be removed "
17241735 "in a future release. Use parse_string(s, strict=False, system=system) "
@@ -1842,8 +1853,7 @@ def cli_script_main(cli_args):
18421853
18431854
18441855def cli_script (): # pragma: no cover
1845- # Wrapper around cli_script_main so we can unittest the command
1846- # line functionality
1856+ """Entry point for the bitmath CLI; wraps cli_script_main for testability."""
18471857 for result in cli_script_main (sys .argv [1 :]):
18481858 print (result )
18491859
0 commit comments