math.sqrt
, requests.get
'string\n'.strip()
, datetime.fromtimestamp
dir(my_module)
relativedelta signature:
def relativedelta(dt1: datetime=None, dt2:datetime=None,
years: int=0, months: int=0, ...,
year: int=0, month: int=0, ...) -> relativedelta:
...
Intended use:
relativedelta(dt1: datetime=None, dt2: datetime=None) -> relativedelta
or
relativedelta(years: int=0, months: int=0, ...,
year: int=0, month: int=0, ...) -> relativedelta
def send_request(req: RequestType,
return_context: bool=False) -> Union[ResponseType, Tuple[ResponseType, Context]]:
...
from dateutil.parser import parse, isoparse
from datetime import datetime
for dt in [parse('March 19, 1951'),
parse('2019-01-04T12:30Z'),
parse('2018/03/01')]:
print(dt)
1951-03-19 00:00:00 2019-01-04 12:30:00+00:00 2018-03-01 00:00:00
isoparse('2019-01-04T12:30Z')
datetime.datetime(2019, 1, 4, 12, 30, tzinfo=tzutc())
try:
isoparse('2018/03/01')
except Exception as e:
print(e)
invalid literal for int() with base 10: b'/03'
class Coordinate:
"""Representation of coordinates on a globe in latitude and longitude"""
def __init__(self, lat, long):
self.lat = float(lat)
self.long = float(long)
def __repr__(self):
return (f'{self.__class__.__name__}(' +
','.join(str(v) for v in (self.lat, self.long)) + ')')
def __str__(self):
latstr = '%f%s' % (abs(self.lat), 'N' if self.lat >= 0.0 else 'S')
longstr = '%f%s' % (abs(self.long), 'W' if self.long >= 0.0 else 'E' )
return f'{latstr},{longstr}'
_BaseCoordinate = Coordinate
cities = {
'Tokyo': Coordinate(35.6895, -139.6917),
'Santiago': Coordinate(-33.4489, 70.6693),
'Toronto': Coordinate(43.7001, 79.4163),
}
city_strs = {city: str(coords) for city, coords in cities.items()}
for city, coords in cities.items():
print(f'{city}: {coords}')
Tokyo: 35.689500N,139.691700E Santiago: 33.448900S,70.669300W Toronto: 43.700100N,79.416300W
class Coordinate(_BaseCoordinate):
def __init__(self, coord_str_or_lat, long=None):
if isinstance(coord_str_or_lat, str) and long is None:
coord_str_or_lat, long = self._parse_coord_str(coord_str_or_lat)
self.lat = float(coord_str_or_lat)
self.long = float(long)
@staticmethod
def _parse_coord_str(coord_str):
m = re.match('(?P<lat>\d+\.?\d*)(?P<latdir>N|S)' + ',' +
'(?P<long>\d+\.?\d*)(?P<longdir>W|E)', coord_str.strip())
if m is None:
raise ValueError(f'Invalid coordinates: {coord_str}')
lat = float(m.group('lat')) * (-1 if m.group('latdir') == 'S' else 1)
long = float(m.group('long')) * (-1 if m.group('longdir') == 'E' else 1)
return lat, long
Coordinate(-27.4698, -153.0251)
Coordinate(-27.4698,-153.0251)
Coordinate('35.689500N,139.691700E')
Coordinate(35.6895,-139.6917)
Coordinate(coord_str: str) -> Coordinate
Coordinate(lat: float, long: float) -> Coordinate
Coordinate(coord_str_or_lat: Union[float, str], long: float=None) -> Coordinate
try:
# long is a valid argument, why is it complaining about converting the coords to floats?!
Coordinate('35.689500N', -139.00)
except Exception as e:
print(repr(e))
ValueError("could not convert string to float: '35.689500N'")
try:
# Why does long have a default value if it's a required argument?
Coordinate(-18.45)
except Exception as e:
print(repr(e))
TypeError("float() argument must be a string or a number, not 'NoneType'")
class Coordinate(_BaseCoordinate):
def __init__(self, lat: float, long: float):
self.lat = float(lat)
self.long = float(long)
@classmethod
def from_str(cls, coord_str: str) -> Coordinate:
m = re.match('(?P<lat>\d+\.?\d*)(?P<latdir>N|S)' + ',' +
'(?P<long>\d+\.?\d*)(?P<longdir>W|E)', coord_str.strip())
if m is None:
raise ValueError(f'Invalid coordinates: {coord_str}')
lat = float(m.group('lat')) * (-1 if m.group('latdir') == 'S' else 1)
long = float(m.group('long')) * (-1 if m.group('longdir') == 'E' else 1)
return cls(lat, long)
Coordinate(40.7128, 74.006)
Coordinate(40.7128,74.006)
Coordinate.from_str('40.712800N,74.006000W')
Coordinate(40.7128,74.006)
Coordinate(lat: float, long: float) -> Coordinate
Coordinate.from_str(coord_str: str) -> Coordinate
@dataclass
class Point:
x: float
y: float
@classmethod
def from_polar(cls, *args, **kwargs):
constructor_args = cls.polar_to_cartesian(*args, **kwargs)
return cls(*constructor_args)
@staticmethod
def polar_to_cartesian(r, theta):
x = r * math.cos(theta)
y = r * math.sin(theta)
# Tweak for floating point errors
x, y = map(round_float_errs, (x, y))
return x, y
Point(1.0, 1.0)
Point(x=1.0, y=1.0)
Point.from_polar(math.sqrt(2), math.pi/4)
Point(x=1.0, y=1.0)
@dataclass
class NamedPoint(Point):
x: float
y: float
name: str = None
NamedPoint(0, 0, name="origin")
NamedPoint(x=0, y=0, name='origin')
NamedPoint.from_polar(0, 0)
NamedPoint(x=0.0, y=0.0, name=None)
@dataclass
class NamedPoint(Point):
x: float
y: float
name: str = None
@classmethod
def from_polar(cls, *args, name=None, **kwargs):
rv = super().from_polar(*args, **kwargs) # Initialize the base class
if name is not None: # Customize the subclass functions
rv.name = name
return rv
NamedPoint.from_polar(0, 0, name='origin')
NamedPoint(x=0.0, y=0.0, name='origin')
@dataclass
class Point3D(Point):
x: float
y: float
z: float
@staticmethod
def polar_to_cartesian(r, theta, phi):
x = r * math.sin(theta) * math.cos(phi)
y = r * math.sin(theta) * math.sin(phi)
z = r * math.cos(theta)
x, y, z = map(round_float_errs, (x, y, z))
return x, y, z
Point3D(1, 1, 1)
Point3D(x=1, y=1, z=1)
Point3D.from_polar(math.sqrt(3), math.acos(1/math.sqrt(3)), math.atan(1))
Point3D(x=1.0, y=1.0, z=1.0)
What about function APIs like this?
def print_text(path_or_buffer: Union[str, IO[str]], *args, **kwargs):
if isinstance(path_or_buffer, str):
with open(path_or_buffer, 'r') as f:
print_text(f, *args, **kwargs)
else:
print(path_or_buffer.read())
Or like this?
# dateutil.parser.parse's API
def parse(timestr: str, **kwargs) -> Union[datetime.datetime,
Tuple[datetime.datetime, Tuple[str, ...]]]:
...
dateutil.parser.parse('John Ford was born May 13, 1951 in Remlap, Alabama', fuzzy=True)
datetime.datetime(1951, 5, 13, 0, 0)
dateutil.parser.parse('John Ford was born May 13, 1951 in Remlap, Alabama', fuzzy_with_tokens=True)
(datetime.datetime(1951, 5, 13, 0, 0), ('John Ford was born ', ' ', ' ', 'in Remlap, Alabama'))
variants
¶print_text(txt: str)
- Print text passed to this functionprint_text.from_stream(sobj: IO[str])
- Print text from a streamprint_text.from_path(path_components: str)
- Open a file on disk and print its contentsimport variants
@variants.primary
def print_text(txt: str):
"""Prints any text passed to this function"""
print(txt)
@print_text.variant('from_stream')
def print_text(sobj: IO[str]):
"""Read text from a stream and print it"""
print_text(sobj.read())
import pathlib
@print_text.variant('from_path')
def print_text(path_components: Union[str, pathlib.Path]):
"""Open the file specified by `path_components` and print the contents"""
fpath = pathlib.Path(path_components)
with open(fpath, 'r') as f:
print_text.from_stream(f)
print_text('Hello, world')
Hello, world
print_text.from_stream(StringIO('Hello, world! This is from a stream!'))
Hello, world! This is from a stream!
print_text.from_path('extras/hello_world.txt')
Hello, world! This is from a file!
@print_text.variant('from_url')
def print_text(url):
r = requests.get(url)
print_text(r.text)
print_text('Hello, world!')
print_text.from_path('extras/hello_world.txt')
print_text.from_url('https://ganssle.io/files/hello_world.txt')
Hello, world! Hello, world! This is from a file! Hello, world! (from url)
singledispatch
¶@functools.singledispatch
def print_text_sd(txt: str):
print(txt)
from pathlib import Path
@print_text_sd.register(Path)
def _(path: Path):
print_text.from_path(path)
@dataclass
class Url:
url: str
@print_text_sd.register(Url)
def _(url: Url):
print_text.from_url(url.url)
print_text_sd('Hello, world!')
print_text_sd(Path('extras/hello_world.txt'))
print_text_sd(Url('https://ganssle.io/files/hello_world.txt'))
Hello, world! Hello, world! This is from a file! Hello, world! (from url)
@variants.primary
@functools.singledispatch
def print_text(txt: str):
print(txt)
@print_text.variant('from_path') # Register the URL variant explicitly
@print_text.register(Path) # And with singledispatch!
def print_text(pth: Union[Path, str]):
with open(pth, 'rt') as f:
print_text(f.read())
print_text("Hello, world!")
Hello, world!
print_text(Path("extras/hello_world.txt"))
print_text.from_path("extras/hello_world.txt")
Hello, world! This is from a file! Hello, world! This is from a file!
try:
print_text.from_path("Hello, world")
except Exception as e:
print(e)
[Errno 2] No such file or directory: 'Hello, world'
dtstr = 'It was 3AM on Sept 21, 1986'
dateutil.parser.parse(dtstr, fuzzy=True)
datetime.datetime(1986, 9, 21, 3, 0)
dateutil.parser.parse(dtstr, fuzzy_with_tokens=True)
(datetime.datetime(1986, 9, 21, 3, 0), ('It was ', ' on ', ' ', ' '))
@variants.primary
def fuzzy_parse(dtstr: str, *args, **kwargs) -> datetime:
kwargs['fuzzy'] = True
return dateutil.parser.parse(dtstr, *args, **kwargs)
@fuzzy_parse.variant('with_tokens')
def fuzzy_parse(dtstr: str, *args, **kwargs) -> Tuple[datetime, Tuple[str, ...]]:
kwargs['fuzzy_with_tokens'] = True
return dateutil.parser.parse(dtstr, *args, **kwargs)
fuzzy_parse(dtstr)
datetime.datetime(1986, 9, 21, 3, 0)
fuzzy_parse.with_tokens(dtstr)
(datetime.datetime(1986, 9, 21, 3, 0), ('It was ', ' on ', ' ', ' '))
from functools import lru_cache
@variants.primary
@lru_cache()
def get_config():
return get_config.nocache()
@get_config.variant('nocache')
def get_config():
print("Retrieving configuration!")
return {
'a1': 'value',
'b': 12348
}
a = get_config()
Retrieving configuration!
b = get_config()
c = get_config.nocache()
Retrieving configuration!
import asyncio
import time
@variants.primary
async def myfunc(n):
for i in range(n):
yield i
await asyncio.sleep(0.5)
@myfunc.variant('sync')
def myfunc(n):
for i in range(n):
yield i
time.sleep(0.5)
myfunc(4)
<async_generator object myfunc at 0x7f0dcaf8fd40>
myfunc.sync(4)
<generator object myfunc at 0x7f0dc9303050>
def coordinate_from_string(coord_str: str) -> Coordinate:
return Coordinate.from_str(coord_str)
def print_text_from_path(fpath: Union[str, pathlib.Path]):
print_text.from_path(fpath)
print_text_from_path('extras/hello_world.txt')
Hello, world! This is from a file!
Using the sphinx_autodoc_variants
sphinx extension:
.. automodule:: text_print
:members:
Talk.conclude()
¶Talk.conclude.with_plugs()
¶variants
:¶