Package: pynumparser
SummaryThis library provides two classes for parsing simple integer or floating piont numbers and numeric
sequences, optionally enforcing value limits. These can be used as explicit objects or as thetypeparameter of an argument within anargparse.ArgumentParser.NumberSequenceparses an input string and yields a sequence of numbers, optionally ensuring
they are within the given limits. It will also convert a sequence of numbers into the string form[as of version 1.2].Numberparses an input string and returns a single number; ensuring they are within the given
limits.Both classes take anumtypeparameter (intorfloat), and alimitsparameter which
can beNoneor a 2-tuple; the 2-tuple imposes limits on the parsed values.InstallationInstall the package usingpip, eg:sudo pip install pynumparserOr for a specific version:sudo python3 -m pip install pynumparserNumberSequenceSyntaxAllowed inputs are comprised of one (1) or more subsequences separated by a comma (“,”).
Subsequences can be simple numbers or number ranges with or without astridevalue.Simple number values yield a single value.A range is expressed as two (2) number values separated by either a dash/hyphen (“-”) or a plus
sign (“+”)[as of version 1.3], optionally followed by a slash (“/”) and astridevalue.A range will usually yield multiple valuesincluding both bounds.  This is in contrast to
the builtin Pythonrange()behaviour.Alower boundandupper boundmay be separated by a single dash/hyphen (“-“).  Note
that it is legal for the upper bound be negative (eg: “-5–3”).Alower boundandrange sizemay be separated by a single plus sign (“+”).  Note that
it is legal for the range size be start with a plus sign (eg: “8++5” is equivalent to “8+5”).[As of version 1.3]Only monotonically increasing ranges are allowed:Anupper rangevalue must not be less than the lower range value (eg: “5-4” is illegal).Thestridemust be positive (eg: “5-8/0” and “8-1/-2” are illegal).Arange sizemust not be negative (eg: “8+-4” is illegal)[as of version 1.3].By default numbers are ofinttype. But ifnumtype=floatis passed to the constructor, the
inputs are parsed as floating point numberswith a dot/peroid for a decimial mark.  In other
words, the representation of5/4must be “1.2” and not “1,2” since the comma is used as the
subsequence separator.If the difference between the limits is not an even mulitiple of thestridevalue, then the
second range willnotbe included in the result.The parser has acontainsmethod, which can be used to check if a number is in the range.[as of version 1.1].TheNumberclass has acontainsmethod, which can be check if a number is in the range.[as of version 1.4.1].NumberSequencehas a classmethodencodethat will convert a sequence into a simplified text
representation.[as of version 1.2].>>>importpynumparser>>>pynumparser.NumberSequence.encode([1,2,3,7,13,19,25])'1-3,7-25/6'>>>pynumparser.NumberSequence.encode([1.00,1.25,1.5,1.75,2,2.25])'1.0-2.25/0.25'>>>pynumparser.NumberSequence.encode(range(10,100,5))'10-95/5'ExceptionsThese apply to both theNumberSequenceandNumberclasses:If the optional constructorlimitsparameter is provided, then aValueErrorwill be raised
in the constructor if any of the following are violated.Thelimitsparameter must be eitherNoneor else a tuple with two (2) values, alower
limitthen anupper limit.Both of thelimitvalues may be eitherNoneor a valid value of thenumtype.Iflower limitandupper limitare numbers, then thelower limitmust be less than theupper limit.Iflimitsparameter was provided to the constructor, then aValueErrorwill be raised
during parsing if any of the following are violated.If thelower limitis a number, then parsed values must not be less than thelower limit.If theupper limitis a number, then parsed values must not be greater than theupper limit.If any input cannot be parsed as a valid number of given thenumtypeaValueErroris raised.If any floating point number equates to positive or negative infinity (eg:“1e9999”) aValueErroris raised.These apply only to theNumberSequenceclass, during parsing:If theupper boundis less than thelower bound(eg: “8-5”) or[as of version 1.3]if therange sizeis negative (eg: “8+-3”), then aValueErroris raised.If thestridevalue is zero or negative,ValueErroris raised, even if the upper and lower
limit values are equal (eg: “8-8/0”).If used within anargparse.ArgumentParser, invalid input will raise aValueErrorand result in
an error message indicating the specific problem, such as:$demo.py--fnum1e20usage:demo.py[-h][-iISEQ][-fFSEQ][-IINUM][-FFNUM]demo.py:error:argument-F/--fnum:invalidFloat(from-100to1000),ERROR:"Too High"value:'1e20'$demo.py-i200-100usage:demo.py[-h][-iISEQ][-fFSEQ][-IINUM][-FFNUM]demo.py:error:argument-i/--iseq:invalidIntSequence(atleast-1000),ERROR:"UPPER<LOWER"value:'200-100'Note that a deficiency in theargparse.ArgumentParserpackage can cause problems with legal
values that start with a dash, even for flags with mandatory arguments.  Although not always true,
for some values (eg: “-1e5”) theargparserpackage will incorrectly abort with an error
message of “expected on argument”.To demonstrate (using code fromExamplesection saved as “demo.py”) for a short flag with a valid
argument and a long flag with an invalid one:$demo.py-f-1e2+2usage:demo.py[-h][-iISEQ][-fFSEQ][-IINUM][-FFNUM]demo.py:error:argument-f/--fseq:expectedoneargument$demo.py-f-1e2+2Namespace(fnum=[],fseq=(-100.0,-99.0,-98.0),inum=[],iseq=[])$demo.py--fnum-1..5usage:demo.py[-h][-iISEQ][-fFSEQ][-IINUM][-FFNUM]demo.py:error:argument-F/--fnum:expectedoneargument$demo.py--fnum=-1..5usage:demo.py[-h][-iISEQ][-fFSEQ][-IINUM][-FFNUM]demo.py:error:argument-F/--fnum:invalidFloat(from-100to1000),ERROR:"Parse Error"value:'-1..5'Example withargparse.ArgumentParserimport argparse
import pynumparser

# Note:  Typical values would likely include 'help' and  'default' parameters.
parser = argparse.ArgumentParser(description="Number printer")

# Add a simple int parameter, requiring it be between -40 and 130, inclusive:
parser.add_argument('-a', '--age', type=pynumparser.Number(limits=(-40, 130)))

# Add int sequence, requiring values to be non-negative:
parser.add_argument('-i', '--ints', type=pynumparser.NumberSequence(limits=(0, None)))

# Add a simple float parameter, requiring it be a positive value less than 1000:
parser.add_argument('-s', '--seconds', type=pynumparser.Number(numtype=float, limits=(1e-230, 1000)))

# Add a float sequence, requiring the numbers be between 0 and 365.25 inclusive:
parser.add_argument('-d', '--days', type=pynumparser.NumberSequence(numtype=float, limits=(0, 365.25)))

print(parser.parse_args())ExamplesNumberSequenceResultsWith the default parameters (numtype=int, limits=None):“5”yields a result of(5).“5-8”is equivalent to“5-8/1”and both yield a result of(5, 6, 7, 8).“3-9/3”would give a result of(3, 6, 9).“-3-2”would yield a result of(-3, -2, -1, 0, 1, 2).“-3–2”would yield a result of(-3, -2).“-5-5/5”would yield a result of(-5, 0, 5).“-8,-9-9/6,12-30/12,5,2,3”would yield(-8, -9, -3, 3, 9, 12, 24, 5, 2, 3).With parameters (numtype=float,limits=None) the results are floating point numbers:“5.125”yields a result of(5.125).“5,125”yields a result of(5.0, 125.0)since the comma is a subsequence separator.“5-7”is equivalent to“5-7/1”and both yield a result of(5.0, 6.0, 7.0).“0-1/.25”would give a result of(0.0, 0.25, 0.5, 0.75, 1.0).ReleasesVersionDescription1.0.1Initial release1.1Added theNumberSequence.contains()method.1.2Added theNumberSequence.encode()method, fixed documentation on PyPi.1.3Added theNumberSequenceformat “+” to specify arange size.1.4Added theNumber.contains()method and Travis CI test integration.1.4.1Moved project to host at Gitlab.Build StatusKnown IssuesUnder some circumstances, floating point representation errors cause the upper range to be
(unexpectedly) omitted.  This happens due to the internal representation of floating point
numbers, and is not limited to this package, or even to Python.  For more information, see:Floating Point Arithmetic: Issues and Limitations.In the first example (“0-13/1.3”), the value of13is not included since the previous
value was larger than11.7.In the second example (“1.2-2.0/0.2”) the final value is slightly less than2.0due to representation error.>>>importpynumparser>>>parser=pynumparser.NumberSequence(float)>>>parser.parse("0-13/1.3")(0.0,1.3,2.6,3.9000000000000004,5.2,6.5,7.8,9.1,10.4,11.700000000000001)>>>parser.parse("1.2-2.0/0.2")(1.2,1.4,1.5999999999999999,1.7999999999999998,1.9999999999999998)