En Python, un contexto (context) es una clase que implementa los métodos ‘__enter__’ y ‘__exit__’ los cuales son llamados si la clase en llamada con la palabra reservada ‘with’. Por ejemplo, los descriptores de archivo (file handle) en Python se pueden llamar con un contexto, ahorrando llamar ‘finally’ para cerrar archivos, sin importar si hay un error.
Les traigo de vuelta el programa de ‘DolarToday’ el cual escribe datos en un archivo binario, pero ahora utilizando contextos:
#!/usr/bin/env python3
# Simple program to save the 'Dolartoday' extra official dollar rates from CSV to a custom format
# Revisited to use 'contexts' to avoid using finally on exception handling
# @author Jose Vicente Nunez, josevnz@kodegeek.com
import os, sys, datetime, re, gzip, struct
from optparse import OptionParser, OptionValueError
from datetime import datetime
class DollarToday:
def __init__(self, ddate, value):
if isinstance(ddate, datetime):
self.__date = ddate
else:
self.__date = datetime.strptime(ddate, "%m-%d-%Y")
self.__value = float(value)
assert self.__value > 0.0, "{} for date {} is invalid!".format(value, self.__date)
@property
def date(self):
return self.__date
@date.setter
def date(self, date):
assert isinstance(date, datetime), "Invalid date {}".format(date)
self.__date = date
@property
def value(self):
return self.__value
def __str__(self):
return "DollarToday[date={}, value={}]".format(self.__date, self.__value)
def __hash__(self):
return str(id(self))
class DollarCollection(dict):
# https://en.wikipedia.org/wiki/List_of_file_signatures
__FILE_MAGIC = b"DLR\x00"
__FILE_VERSION = b"\x00\x01"
__dollarStruct = struct.Struct(" self.__FILE_VERSION:
raise "Unsupported file version: {}, expected {}".format(version, self.__FILE_VERSION)
self.clear()
while True:
data = fh.read(self.__dollarStruct.size)
if len(data) == 0:
break
numbers = self.__dollarStruct.unpack(data)
#if verbose:
# print("{}".format(",".join([str(x) for x in numbers])))
dolar = DollarToday(
datetime.fromordinal(numbers[0]),
numbers[1]
)
self[dolar.date] = dolar
except (Exception) as err:
raise
def readCsv(self, file):
tempMap = {}
try:
with open(file, "r", encoding="UTF-8") as fh:
for line in fh.readlines():
# 6-23-2010 9.92
(date, value) = line.strip().split("\t")
if re.match("Fecha", date):
continue
try:
dolVsBol = DollarToday(date, value)
tempMap[dolVsBol.date] = dolVsBol
except (Exception) as err:
print(err)
self.clear()
self.update(tempMap)
except (Exception) as err:
raise
def __str__(self):
return "Records={},\n{}".format(len(self), ",\n".join([str(date) for date in self.values()]))
def main(options):
verbose = options.verbose
dc = DollarCollection()
if options.report == None:
dc.readCsv(options.read)
dc.save(options.write)
else:
dc.readBinary(options.report, verbose)
if verbose:
print("{}".format(dc))
if __name__ == "__main__":
usagetext = """
%prog --read csv.file --write binary.file
Or:
%prog --report binary.file
"""
op = OptionParser(usage=usagetext)
op.add_option(
"-r", "--read",
action="store",
dest="read",
help="Full path to CSV file with date,value pairs per line")
op.add_option(
"-w", "--write",
action="store",
dest="write",
help="Full path to destination file in binary format")
op.add_option(
"-p", "--report",
action="store",
dest="report",
help="Read the contents of the binary storage and generate a report. Incompatible with --read and --write")
op.add_option(
"-v", "--verbose",
action="store_true",
default=False,
dest="verbose",
help="Enable verbose mode")
(options, values) = op.parse_args()
main(options)