diff --git a/edtf/parser/grammar_test.py b/edtf/parser/grammar_test.py deleted file mode 100644 index c8ff727..0000000 --- a/edtf/parser/grammar_test.py +++ /dev/null @@ -1,360 +0,0 @@ -from pyparsing import ( - Combine, - NotAny, - OneOrMore, - Optional, - ParseException, - Regex, - Word, - ZeroOrMore, - nums, - oneOf, -) -from pyparsing import Literal as L - -from edtf.parser.edtf_exceptions import EDTFParseException - -# (* ************************** Level 0 *************************** *) -from edtf.parser.parser_classes import ( - UA, - Consecutives, - Date, - DateAndTime, - EarlierConsecutives, - ExponentialYear, - Interval, - LaterConsecutives, - Level1Interval, - Level2Interval, # , Testi - LongYear, - MultipleDates, - OneOfASet, - PartialUncertainOrApproximate, - PartialUnspecified, - Season, - UncertainOrApproximate, - Unspecified, -) - -oneThru12 = oneOf(["%.2d" % i for i in range(1, 13)]) -oneThru13 = oneOf(["%.2d" % i for i in range(1, 14)]) -oneThru23 = oneOf(["%.2d" % i for i in range(1, 24)]) -zeroThru23 = oneOf(["%.2d" % i for i in range(0, 24)]) -oneThru29 = oneOf(["%.2d" % i for i in range(1, 30)]) -oneThru30 = oneOf(["%.2d" % i for i in range(1, 31)]) -oneThru31 = oneOf(["%.2d" % i for i in range(1, 32)]) -oneThru59 = oneOf(["%.2d" % i for i in range(1, 60)]) -zeroThru59 = oneOf(["%.2d" % i for i in range(0, 60)]) - -positiveDigit = Word(nums, exact=1, excludeChars="0") -digit = Word(nums, exact=1) - -second = zeroThru59 -minute = zeroThru59 -hour = zeroThru23 -day = oneThru31("day") - -month = oneThru12("month") -monthDay = ( - (oneOf("01 03 05 07 08 10 12")("month") + "-" + oneThru31("day")) - ^ (oneOf("04 06 09 11")("month") + "-" + oneThru30("day")) - ^ (L("02")("month") + "-" + oneThru29("day")) -) - -# 4 digits, 0 to 9 -positiveYear = Word(nums, exact=4) - -# Negative version of positive year, but "-0000" is illegal -negativeYear = NotAny(L("-0000")) + ("-" + positiveYear) - -year = Combine(positiveYear ^ negativeYear)("year") - -yearMonth = year + "-" + month -yearMonthDay = year + "-" + monthDay # o hai iso date - -date = Combine(year ^ yearMonth ^ yearMonthDay)("date") -Date.set_parser(date) - -zoneOffsetHour = oneThru13 -zoneOffset = L("Z") ^ ( - Regex("[+-]") - + (zoneOffsetHour + Optional(":" + minute) ^ L("14:00") ^ ("00:" + oneThru59)) -) - -baseTime = Combine(hour + ":" + minute + ":" + second ^ "24:00:00") - -time = Combine(baseTime + Optional(zoneOffset))("time") - -dateAndTime = date + "T" + time -DateAndTime.set_parser(dateAndTime) - -l0Interval = date("lower") + "/" + date("upper") -Interval.set_parser(l0Interval) - -level0Expression = date ^ dateAndTime ^ l0Interval - - -# (* ************************** Level 1 *************************** *) - -# (* ** Auxiliary Assignments for Level 1 ** *) -UASymbol = Combine(oneOf("? ~ %")) -UA.set_parser(UASymbol) - -seasonNumber = oneOf("21 22 23 24") - -# (* *** Season (unqualified) *** *) -season = year + "-" + seasonNumber("season") -Season.set_parser(season) - -dateOrSeason = date("") ^ season - -# (* *** Long Year - Simple Form *** *) - -longYearSimple = "Y" + Combine( - Optional("-") + positiveDigit + digit + digit + digit + OneOrMore(digit) -)("year") -LongYear.set_parser(longYearSimple) - -# (* *** L1Interval *** *) -uaDateOrSeason = dateOrSeason + Optional(UASymbol) - - -# unspecifiedIntervalSec = L('..')('unknownOrOpen') + FollowedBy(L("/") + uaDateOrSeason)('other_section_element') -# Testi.set_parser(unspecifiedIntervalSec) - - -# bit of a kludge here to get the all the relevant tokens into the parse action -# cleanly otherwise the parameter names are overlapped. -def f(toks): - try: - return {"date": toks[0], "ua": toks[1]} - except IndexError: - return {"date": toks[0], "ua": None} - - -l1Start = ".." ^ uaDateOrSeason -# l1Start = unspecifiedIntervalSec ^ uaDateOrSeason -l1Start.addParseAction(f) -l1End = uaDateOrSeason ^ ".." -l1End.addParseAction(f) - -# level1Interval = l1Start("lower") + "/" + l1End("upper") -level1Interval = Optional(l1Start)("lower") + "/" + l1End("upper") ^ l1Start( - "lower" -) + "/" + Optional(l1End("upper")) -Level1Interval.set_parser(level1Interval) - -# (* *** unspecified *** *) -yearWithOneOrTwoUnspecifedDigits = Combine(digit + digit + (digit ^ "X") + "X")("year") -monthUnspecified = year + "-" + L("XX")("month") -dayUnspecified = yearMonth + "-" + L("XX")("day") -dayAndMonthUnspecified = year + "-" + L("XX")("month") + "-" + L("XX")("day") - -unspecified = ( - yearWithOneOrTwoUnspecifedDigits - ^ monthUnspecified - ^ dayUnspecified - ^ dayAndMonthUnspecified -) -Unspecified.set_parser(unspecified) - -# (* *** uncertainOrApproxDate *** *) - -uncertainOrApproxDate = date("date") + UASymbol("ua") -UncertainOrApproximate.set_parser(uncertainOrApproxDate) - -level1Expression = ( - uncertainOrApproxDate ^ unspecified ^ level1Interval ^ longYearSimple ^ season -) - -# (* ************************** Level 2 *************************** *) - -# (* ** Internal Unspecified** *) - -digitOrU = Word(nums + "X", exact=1) - -# 2-digit day with at least one 'X' present -dayWithU = Combine(("X" + digitOrU) ^ (digitOrU + "X"))("day") - -# 2-digit month with at least one 'X' present -monthWithU = Combine(oneOf("0X 1X") ^ ("X" + digitOrU))("month") - -# 4-digit year with at least one 'X' present -yearWithU = Combine( - ("X" + digitOrU + digitOrU + digitOrU) - ^ (digitOrU + "X" + digitOrU + digitOrU) - ^ (digitOrU + digitOrU + "X" + digitOrU) - ^ (digitOrU + digitOrU + digitOrU + "X") -)("year") - -yearMonthWithU = (Combine(year("") ^ yearWithU(""))("year") + "-" + monthWithU) ^ ( - yearWithU + "-" + month -) - -monthDayWithU = (Combine(month("") ^ monthWithU(""))("month") + "-" + dayWithU) ^ ( - monthWithU + "-" + day -) - -yearMonthDayWithU = ( - ( - yearWithU - + "-" - + Combine(month("") ^ monthWithU(""))("month") - + "-" - + Combine(day("") ^ dayWithU(""))("day") - ) - ^ (year + "-" + monthWithU + "-" + Combine(day("") ^ dayWithU(""))("day")) - ^ (year + "-" + month + "-" + dayWithU) -) - -partialUnspecified = yearWithU ^ yearMonthWithU ^ yearMonthDayWithU -PartialUnspecified.set_parser(partialUnspecified) - -# (* ** Internal Uncertain or Approximate** *) - -# this line is out of spec, but the given examples (e.g. '(2004)?-06-04~') -# appear to require it. -year_with_brackets = year ^ ("(" + year + ")") - -# second clause below needed Optional() around the "year_ua" UASymbol, for dates -# like '(2011)-06-04~' to work. - -IUABase = ( - ( - year_with_brackets - + UASymbol("year_ua") - + "-" - + month - + Optional("-(" + day + ")" + UASymbol("day_ua")) - ) - ^ ( - year_with_brackets - + Optional(UASymbol)("year_ua") - + "-" - + monthDay - + Optional(UASymbol)("month_day_ua") - ) - ^ ( - year_with_brackets - + Optional(UASymbol)("year_ua") - + "-(" - + month - + ")" - + UASymbol("month_ua") - + Optional("-(" + day + ")" + UASymbol("day_ua")) - ) - ^ ( - year_with_brackets - + Optional(UASymbol)("year_ua") - + "-(" - + month - + ")" - + UASymbol("month_ua") - + Optional("-" + day) - ) - ^ (yearMonth + UASymbol("year_month_ua") + "-(" + day + ")" + UASymbol("day_ua")) - ^ (yearMonth + UASymbol("year_month_ua") + "-" + day) - ^ (yearMonth + "-(" + day + ")" + UASymbol("day_ua")) - ^ (year + "-(" + monthDay + ")" + UASymbol("month_day_ua")) - ^ (season("ssn") + UASymbol("season_ua")) -) - -partialUncertainOrApproximate = IUABase ^ ("(" + IUABase + ")" + UASymbol("all_ua")) -PartialUncertainOrApproximate.set_parser(partialUncertainOrApproximate) - -dateWithInternalUncertainty = partialUncertainOrApproximate ^ partialUnspecified - -qualifyingString = Regex(r"\S") # any nonwhitespace char - -# (* ** SeasonQualified ** *) -seasonQualifier = qualifyingString -seasonQualified = season + "^" + seasonQualifier - -# (* ** Long Year - Scientific Form ** *) -positiveInteger = Combine(positiveDigit + ZeroOrMore(digit)) -longYearScientific = ( - "Y" - + Combine(Optional("-") + positiveInteger)("base") - + "E" - + positiveInteger("exponent") - + Optional("S" + positiveInteger("precision")) -) -ExponentialYear.set_parser(longYearScientific) - -# (* ** level2Interval ** *) -level2Interval = ( - (dateOrSeason("lower") + "/" + dateWithInternalUncertainty("upper")) - ^ (dateWithInternalUncertainty("lower") + "/" + dateOrSeason("upper")) - ^ ( - dateWithInternalUncertainty("lower") - + "/" - + dateWithInternalUncertainty("upper") - ) -) -Level2Interval.set_parser(level2Interval) - -# (* ** Masked precision ** *) eliminated in latest specs -# maskedPrecision = Combine(digit + digit + ((digit + "x") ^ "xx"))("year") -# MaskedPrecision.set_parser(maskedPrecision) - -# (* ** Inclusive list and choice list** *) -consecutives = ( - (yearMonthDay("lower") + ".." + yearMonthDay("upper")) - ^ (yearMonth("lower") + ".." + yearMonth("upper")) - ^ (year("lower") + ".." + year("upper")) -) -Consecutives.set_parser(consecutives) - -listElement = ( - date - ^ dateWithInternalUncertainty - ^ uncertainOrApproxDate - ^ unspecified - ^ consecutives -) - -earlier = ".." + date("upper") -EarlierConsecutives.set_parser(earlier) -later = date("lower") + ".." -LaterConsecutives.set_parser(later) - -listContent = ( - (earlier + ZeroOrMore("," + listElement)) - ^ (Optional(earlier + ",") + ZeroOrMore(listElement + ",") + later) - ^ (listElement + OneOrMore("," + listElement)) - ^ consecutives -) - -choiceList = "[" + listContent + "]" -OneOfASet.set_parser(choiceList) - -inclusiveList = "{" + listContent + "}" -MultipleDates.set_parser(inclusiveList) - -level2Expression = ( - partialUncertainOrApproximate - ^ partialUnspecified - ^ choiceList - ^ inclusiveList - ^ level2Interval - ^ longYearScientific - ^ seasonQualified -) - -# putting it all together -edtfParser = ( - level0Expression("level0") ^ level1Expression("level1") ^ level2Expression("level2") -) - - -def parse_edtf(str, parseAll=True, fail_silently=False): - try: - if not str: - raise ParseException("You must supply some input text") - p = edtfParser.parseString(str.strip(), parseAll) - if p: - return p[0] - except ParseException as err: - if fail_silently: - return None - raise EDTFParseException(err) from err diff --git a/edtf/parser/parser_classes_tests.py b/edtf/parser/parser_classes_tests.py deleted file mode 100644 index 857d0f6..0000000 --- a/edtf/parser/parser_classes_tests.py +++ /dev/null @@ -1,834 +0,0 @@ -# ruff: noqa: S101 # Asserts are ok in tests - -import calendar -import re -from datetime import date, datetime -from operator import add, sub -from time import struct_time - -from dateutil.relativedelta import relativedelta - -from edtf import appsettings -from edtf.convert import ( - TIME_EMPTY_EXTRAS, - TIME_EMPTY_TIME, - dt_to_struct_time, - trim_struct_time, -) - -EARLIEST = "earliest" -LATEST = "latest" - -PRECISION_MILLENIUM = "millenium" -PRECISION_CENTURY = "century" -PRECISION_DECADE = "decade" -PRECISION_YEAR = "year" -PRECISION_MONTH = "month" -PRECISION_SEASON = "season" -PRECISION_DAY = "day" - - -def days_in_month(year, month): - """ - Return the number of days in the given year and month, where month is - 1=January to 12=December, and respecting leap years as identified by - `calendar.isleap()` - """ - return { - 1: 31, - 2: 29 if calendar.isleap(year) else 28, - 3: 31, - 4: 30, - 5: 31, - 6: 30, - 7: 31, - 8: 31, - 9: 30, - 10: 31, - 11: 30, - 12: 31, - }[month] - - -def apply_delta(op, time_struct, delta): - """ - Apply a `relativedelta` to a `struct_time` data structure. - - `op` is an operator function, probably always `add` or `sub`tract to - correspond to `a_date + a_delta` and `a_date - a_delta`. - - This function is required because we cannot use standard `datetime` module - objects for conversion when the date/time is, or will become, outside the - boundary years 1 AD to 9999 AD. - """ - if not delta: - return time_struct # No work to do - - try: - dt_result = op(datetime(*time_struct[:6]), delta) - return dt_to_struct_time(dt_result) - except (OverflowError, ValueError): - # Year is not within supported 1 to 9999 AD range - pass - - # Here we fake the year to one in the acceptable range to avoid having to - # write our own date rolling logic - - # Adjust the year to be close to the 2000 millenium in 1,000 year - # increments to try and retain accurate relative leap years - actual_year = time_struct.tm_year - millenium = int(float(actual_year) / 1000) - millenium_diff = (2 - millenium) * 1000 - adjusted_year = actual_year + millenium_diff - # Apply delta to the date/time with adjusted year - dt = datetime(*(adjusted_year,) + time_struct[1:6]) - dt_result = op(dt, delta) - # Convert result year back to its original millenium - final_year = dt_result.year - millenium_diff - return struct_time( - (final_year,) + dt_result.timetuple()[1:6] + tuple(TIME_EMPTY_EXTRAS) - ) - - -class EDTFObject: - """ - Object to attact to a parser to become instantiated when the parser - completes. - """ - - parser = None - - @classmethod - def set_parser(cls, p): - cls.parser = p - p.addParseAction(cls.parse_action) - - @classmethod - def parse_action(cls, toks): - kwargs = toks.asDict() - try: - return cls(**kwargs) # replace the token list with the class - except Exception as e: - print(f"trying to {cls.__name__}.__init__(**{kwargs})") - raise e - - @classmethod - def parse(cls, s): - return cls.parser.parseString(s)[0] - - def __repr__(self): - return f"{type(self).__name__}: '{str(self)}'" - - def __init__(self, *args, **kwargs): - str = f"{type(self).__name__}.__init__(*{args}, **{kwargs})" - raise NotImplementedError(f"{str} is not implemented.") - - def __str__(self): - raise NotImplementedError - - def _strict_date(self, lean): - raise NotImplementedError - - def lower_strict(self): - return self._strict_date(lean=EARLIEST) - - def upper_strict(self): - return self._strict_date(lean=LATEST) - - def _get_fuzzy_padding(self, lean): - """ - Subclasses should override this to pad based on how precise they are. - """ - return relativedelta(0) - - def get_is_approximate(self): - return getattr(self, "_is_approximate", False) - - def set_is_approximate(self, val): - self._is_approximate = val - - is_approximate = property(get_is_approximate, set_is_approximate) - - def get_is_uncertain(self): - return getattr(self, "_is_uncertain", False) - - def set_is_uncertain(self, val): - self._is_uncertain = val - - is_uncertain = property(get_is_uncertain, set_is_uncertain) - - def get_is_uncertain_and_approximate(self): - return getattr(self, "_uncertain_and_approximate", False) - - def set_is_uncertain_and_approximate(self, val): - self._uncertain_and_approximate = val - - is_uncertain_and_approximate = property( - get_is_uncertain_and_approximate, set_is_uncertain_and_approximate - ) - - def lower_fuzzy(self): - strict_val = self.lower_strict() - return apply_delta(sub, strict_val, self._get_fuzzy_padding(EARLIEST)) - - def upper_fuzzy(self): - strict_val = self.upper_strict() - return apply_delta(add, strict_val, self._get_fuzzy_padding(LATEST)) - - def __eq__(self, other): - if isinstance(other, EDTFObject): - return str(self) == str(other) - elif isinstance(other, date): - return str(self) == other.isoformat() - elif isinstance(other, struct_time): - return self._strict_date() == trim_struct_time(other) - return False - - def __ne__(self, other): - if isinstance(other, EDTFObject): - return str(self) != str(other) - elif isinstance(other, date): - return str(self) != other.isoformat() - elif isinstance(other, struct_time): - return self._strict_date() != trim_struct_time(other) - return True - - def __gt__(self, other): - if isinstance(other, EDTFObject): - return self.lower_strict() > other.lower_strict() - elif isinstance(other, date): - return self.lower_strict() > dt_to_struct_time(other) - elif isinstance(other, struct_time): - return self.lower_strict() > trim_struct_time(other) - raise TypeError( - f"can't compare {type(self).__name__} with {type(other).__name__}" - ) - - def __ge__(self, other): - if isinstance(other, EDTFObject): - return self.lower_strict() >= other.lower_strict() - elif isinstance(other, date): - return self.lower_strict() >= dt_to_struct_time(other) - elif isinstance(other, struct_time): - return self.lower_strict() >= trim_struct_time(other) - raise TypeError( - f"can't compare {type(self).__name__} with {type(other).__name__}" - ) - - def __lt__(self, other): - if isinstance(other, EDTFObject): - return self.lower_strict() < other.lower_strict() - elif isinstance(other, date): - return self.lower_strict() < dt_to_struct_time(other) - elif isinstance(other, struct_time): - return self.lower_strict() < trim_struct_time(other) - raise TypeError( - f"can't compare {type(self).__name__} with {type(other).__name__}" - ) - - def __le__(self, other): - if isinstance(other, EDTFObject): - return self.lower_strict() <= other.lower_strict() - elif isinstance(other, date): - return self.lower_strict() <= dt_to_struct_time(other) - elif isinstance(other, struct_time): - return self.lower_strict() <= trim_struct_time(other) - raise TypeError( - f"can't compare {type(self).__name__} with {type(other).__name__}" - ) - - -# (* ************************** Level 0 *************************** *) - - -class Date(EDTFObject): - def set_year(self, y): - if y is None: - raise AttributeError("Year must not be None") - self._year = y - - def get_year(self): - return self._year - - year = property(get_year, set_year) - - def set_month(self, m): - self._month = m - if m is None: - self.day = None - - def get_month(self): - return self._month - - month = property(get_month, set_month) - - def __init__(self, year=None, month=None, day=None, **kwargs): - for param in ("date", "lower", "upper"): - if param in kwargs: - self.__init__(**kwargs[param]) - return - - self.year = year # Year is required, but sometimes passed in as a 'date' dict. - self.month = month - self.day = day - - def __str__(self): - r = self.year - if self.month: - r += f"-{self.month}" - if self.day: - r += f"-{self.day}" - return r - - def isoformat(self, default=date.max): - return "%s-%02d-%02d" % ( - self.year, - int(self.month or default.month), - int(self.day or default.day), - ) - - def _precise_year(self, lean): - # Replace any ambiguous characters in the year string with 0s or 9s - if lean == EARLIEST: - return int(re.sub(r"X", r"0", self.year)) - else: - return int(re.sub(r"X", r"9", self.year)) - - def _precise_month(self, lean): - if self.month and self.month != "XX": - try: - return int(self.month) - except ValueError as err: - raise ValueError( - f"Couldn't convert {self.month} to int (in {self})" - ) from err - else: - return 1 if lean == EARLIEST else 12 - - def _precise_day(self, lean): - if not self.day or self.day == "XX": - if lean == EARLIEST: - return 1 - else: - return days_in_month( - self._precise_year(LATEST), self._precise_month(LATEST) - ) - else: - return int(self.day) - - def _strict_date(self, lean): - """ - Return a `time.struct_time` representation of the date. - """ - return struct_time( - ( - self._precise_year(lean), - self._precise_month(lean), - self._precise_day(lean), - ) - + tuple(TIME_EMPTY_TIME) - + tuple(TIME_EMPTY_EXTRAS) - ) - - @property - def precision(self): - if self.day: - return PRECISION_DAY - if self.month: - return PRECISION_MONTH - return PRECISION_YEAR - - -class DateAndTime(EDTFObject): - def __init__(self, date, time): - self.date = date - self.time = time - - def __str__(self): - return self.isoformat() - - def isoformat(self): - return self.date.isoformat() + "T" + self.time - - def _strict_date(self, lean): - return self.date._strict_date(lean) - - def __eq__(self, other): - if isinstance(other, datetime): - return self.isoformat() == other.isoformat() - elif isinstance(other, struct_time): - return self._strict_date() == trim_struct_time(other) - return super().__eq__(other) - - def __ne__(self, other): - if isinstance(other, datetime): - return self.isoformat() != other.isoformat() - elif isinstance(other, struct_time): - return self._strict_date() != trim_struct_time(other) - return super().__ne__(other) - - -class Interval(EDTFObject): - def __init__(self, lower, upper): - self.lower = lower - self.upper = upper - - def __str__(self): - return f"{self.lower}/{self.upper}" - - def _strict_date(self, lean): - if lean == EARLIEST: - try: - r = self.lower._strict_date(lean) - if r is None: - raise AttributeError - return r - except ( - AttributeError - ): # it's a string, or no date. Result depends on the upper date - upper = self.upper._strict_date(LATEST) - return apply_delta(sub, upper, appsettings.DELTA_IF_UNKNOWN) - else: - try: - r = self.upper._strict_date(lean) - if r is None: - raise AttributeError - return r - except ( - AttributeError - ): # an 'unknown' or 'open' string - depends on the lower date - if self.upper and (self.upper == "open" or self.upper.date == "open"): - return dt_to_struct_time(date.today()) # it's still happening - else: - lower = self.lower._strict_date(EARLIEST) - return apply_delta(add, lower, appsettings.DELTA_IF_UNKNOWN) - - -# (* ************************** Level 1 *************************** *) - - -class UA(EDTFObject): - @classmethod - def parse_action(cls, toks): - args = toks.asList() - return cls(*args) - - def __init__(self, *args): - assert len(args) == 1 - ua = args[0] - - self.is_uncertain = "?" in ua - self.is_approximate = "~" in ua - self.is_uncertain_and_approximate = "%" in ua - - def __str__(self): - d = "" - if self.is_uncertain: - d += "?" - if self.is_approximate: - d += "~" - if self.is_uncertain_and_approximate: - d += "%" - return d - - def _get_multiplier(self): - if self.is_uncertain_and_approximate: - return appsettings.MULTIPLIER_IF_BOTH - elif self.is_uncertain: - return appsettings.MULTIPLIER_IF_UNCERTAIN - elif self.is_approximate: - return appsettings.MULTIPLIER_IF_APPROXIMATE - - -class UncertainOrApproximate(EDTFObject): - def __init__(self, date, ua): - self.date = date - self.ua = ua - - def __str__(self): - if self.ua: - return f"{self.date}{self.ua}" - else: - return str(self.date) - - def _strict_date(self, lean): - if self.date == "open": - return None # depends on the other date - return dt_to_struct_time(date.today()) - if self.date == "unknown": - return None # depends on the other date - return self.date._strict_date(lean) - - def _get_fuzzy_padding(self, lean): - if not self.ua: - return relativedelta(0) - multiplier = self.ua._get_multiplier() - - if self.date.precision == PRECISION_DAY: - return multiplier * appsettings.PADDING_DAY_PRECISION - elif self.date.precision == PRECISION_MONTH: - return multiplier * appsettings.PADDING_MONTH_PRECISION - elif self.date.precision == PRECISION_YEAR: - return multiplier * appsettings.PADDING_YEAR_PRECISION - - -class Testi(EDTFObject): - # @classmethod - # def parse_action(cls, toks): - # args = toks.asList() - # return cls(*args) - - def __init__(self, **args): - print(args) - - -class UnspecifiedIntervalSection(EDTFObject): - def __init__(self, sectionOpen=False, other_section_element=None): - if sectionOpen: - self.is_open = True - self.is_unknown = False - else: - self.is_open = False - self.is_unknown = True - self.other = other_section_element - - def __str__(self): - if self.is_unknown: - return "" - else: - return ".." - - def _strict_date(self, lean): - if lean == EARLIEST: - if self.is_unknown: - upper = self.other._strict_date(LATEST) - return apply_delta(sub, upper, appsettings.DELTA_IF_UNKNOWN) - else: - return dt_to_struct_time( - date.min - ) # from the beginning of time; *ahem, i mean python datetime - else: - if self.is_unknown: - lower = self.other._strict_date(EARLIEST) - return apply_delta(add, lower, appsettings.DELTA_IF_UNKNOWN) - else: - return dt_to_struct_time(date.max) # to then end of python datetime - - -class Unspecified(Date): - pass - - -class Level1Interval(Interval): - def __init__(self, lower=None, upper=None): - if lower: - if lower["date"] == "..": - self.lower = UnspecifiedIntervalSection( - True, UncertainOrApproximate(**upper) - ) - else: - self.lower = UncertainOrApproximate(**lower) - else: - self.lower = UnspecifiedIntervalSection( - False, UncertainOrApproximate(**upper) - ) - if upper: - if upper["date"] == "..": - self.upper = UnspecifiedIntervalSection( - True, UncertainOrApproximate(**lower) - ) - else: - self.upper = UncertainOrApproximate(**upper) - else: - self.upper = UnspecifiedIntervalSection( - False, UncertainOrApproximate(**lower) - ) - - def _get_fuzzy_padding(self, lean): - if lean == EARLIEST: - return self.lower._get_fuzzy_padding(lean) - elif lean == LATEST: - return self.upper._get_fuzzy_padding(lean) - - -class LongYear(EDTFObject): - def __init__(self, year): - self.year = year - - def __str__(self): - return f"Y{self.year}" - - def _precise_year(self): - return int(self.year) - - def _strict_date(self, lean): - py = self._precise_year() - if lean == EARLIEST: - return struct_time([py, 1, 1] + TIME_EMPTY_TIME + TIME_EMPTY_EXTRAS) - else: - return struct_time([py, 12, 31] + TIME_EMPTY_TIME + TIME_EMPTY_EXTRAS) - - -class Season(Date): - def __init__(self, year, season, **kwargs): - self.year = year - self.season = season # use season to look up month - # day isn't part of the 'season' spec, but it helps the inherited - # `Date` methods do their thing. - self.day = None - - def __str__(self): - return f"{self.year}-{self.season}" - - def _precise_month(self, lean): - rng = appsettings.SEASON_MONTHS_RANGE[int(self.season)] - if lean == EARLIEST: - return rng[0] - else: - return rng[1] - - -# (* ************************** Level 2 *************************** *) - - -class PartialUncertainOrApproximate(Date): - def set_year(self, y): # Year can be None. - self._year = y - - year = property(Date.get_year, set_year) - - def __init__( - self, - year=None, - month=None, - day=None, - year_ua=False, - month_ua=False, - day_ua=False, - year_month_ua=False, - month_day_ua=False, - ssn=None, - season_ua=False, - all_ua=False, - ): - self.year = year - self.month = month - self.day = day - - self.year_ua = year_ua - self.month_ua = month_ua - self.day_ua = day_ua - - self.year_month_ua = year_month_ua - self.month_day_ua = month_day_ua - - self.season = ssn - self.season_ua = season_ua - - self.all_ua = all_ua - - def __str__(self): - if self.season_ua: - return f"{self.season}{self.season_ua}" - - y = f"{self.year}{self.year_ua}" if self.year_ua else str(self.year) - - m = f"({self.month}){self.month_ua}" if self.month_ua else str(self.month) - - if self.day: - d = f"({self.day}){self.day_ua}" if self.day_ua else str(self.day) - else: - d = None - - if self.year_month_ua: # year/month approximate. No brackets needed. - ym = f"{y}-{m}{self.year_month_ua}" - result = f"{ym}-{d}" if d else ym - elif self.month_day_ua: - if self.year_ua: # we don't need the brackets round month and day - result = f"{y}-{m}-{d}{self.month_day_ua}" - else: - result = f"{y}-({m}-{d}){self.month_day_ua}" - else: - result = f"{y}-{m}-{d}" if d else f"{y}-{m}" - - if self.all_ua: - result = f"({result}){self.all_ua}" - - return result - - def _precise_year(self, lean): - if self.season: - return self.season._precise_year(lean) - return super()._precise_year(lean) - - def _precise_month(self, lean): - if self.season: - return self.season._precise_month(lean) - return super()._precise_month(lean) - - def _precise_day(self, lean): - if self.season: - return self.season._precise_day(lean) - return super()._precise_day(lean) - - def _get_fuzzy_padding(self, lean): - """ - This is not a perfect interpretation as fuzziness is introduced for - redundant uncertainly modifiers e.g. (2006~)~ will get two sets of - fuzziness. - """ - result = relativedelta(0) - - if self.year_ua: - result += ( - appsettings.PADDING_YEAR_PRECISION * self.year_ua._get_multiplier() - ) - if self.month_ua: - result += ( - appsettings.PADDING_MONTH_PRECISION * self.month_ua._get_multiplier() - ) - if self.day_ua: - result += appsettings.PADDING_DAY_PRECISION * self.day_ua._get_multiplier() - - if self.year_month_ua: - result += ( - appsettings.PADDING_YEAR_PRECISION - * self.year_month_ua._get_multiplier() - ) - result += ( - appsettings.PADDING_MONTH_PRECISION - * self.year_month_ua._get_multiplier() - ) - if self.month_day_ua: - result += ( - appsettings.PADDING_DAY_PRECISION * self.month_day_ua._get_multiplier() - ) - result += ( - appsettings.PADDING_MONTH_PRECISION - * self.month_day_ua._get_multiplier() - ) - - if self.season_ua: - result += ( - appsettings.PADDING_SEASON_PRECISION * self.season_ua._get_multiplier() - ) - - if self.all_ua: - multiplier = self.all_ua._get_multiplier() - - if self.precision == PRECISION_DAY: - result += multiplier * appsettings.PADDING_DAY_PRECISION - result += multiplier * appsettings.PADDING_MONTH_PRECISION - result += multiplier * appsettings.PADDING_YEAR_PRECISION - elif self.precision == PRECISION_MONTH: - result += multiplier * appsettings.PADDING_MONTH_PRECISION - result += multiplier * appsettings.PADDING_YEAR_PRECISION - elif self.precision == PRECISION_YEAR: - result += multiplier * appsettings.PADDING_YEAR_PRECISION - - return result - - -class PartialUnspecified(Unspecified): - pass - - -class Consecutives(Interval): - # Treating Consecutive ranges as intervals where one bound is optional - def __init__(self, lower=None, upper=None): - if lower and not isinstance(lower, EDTFObject): - self.lower = Date.parse(lower) - else: - self.lower = lower - - if upper and not isinstance(upper, EDTFObject): - self.upper = Date.parse(upper) - else: - self.upper = upper - - def __str__(self): - return "{}..{}".format(self.lower or "", self.upper or "") - - -class EarlierConsecutives(Consecutives): - pass - - -class LaterConsecutives(Consecutives): - pass - - -class OneOfASet(EDTFObject): - @classmethod - def parse_action(cls, toks): - args = [t for t in toks.asList() if isinstance(t, EDTFObject)] - return cls(*args) - - def __init__(self, *args): - self.objects = args - - def __str__(self): - return "[{}]".format(", ".join([str(o) for o in self.objects])) - - def _strict_date(self, lean): - if lean == LATEST: - return max([x._strict_date(lean) for x in self.objects]) - else: - return min([x._strict_date(lean) for x in self.objects]) - - -class MultipleDates(EDTFObject): - @classmethod - def parse_action(cls, toks): - args = [t for t in toks.asList() if isinstance(t, EDTFObject)] - return cls(*args) - - def __init__(self, *args): - self.objects = args - - def __str__(self): - return "{{{}}}".format(", ".join([str(o) for o in self.objects])) - - def _strict_date(self, lean): - if lean == LATEST: - return max([x._strict_date(lean) for x in self.objects]) - else: - return min([x._strict_date(lean) for x in self.objects]) - - -class MaskedPrecision(Date): - pass - - -class Level2Interval(Level1Interval): - def __init__(self, lower, upper): - # Check whether incoming lower/upper values are single-item lists, and - # if so take just the first item. This works around what I *think* is a - # bug in the grammer that provides us with single-item lists of - # `PartialUncertainOrApproximate` items for lower/upper values. - if isinstance(lower, (tuple, list)) and len(lower) == 1: - self.lower = lower[0] - else: - self.lower = lower - if isinstance(lower, (tuple, list)) and len(upper) == 1: - self.upper = upper[0] - else: - self.upper = upper - - -class ExponentialYear(LongYear): - def __init__(self, base, exponent, precision=None): - self.base = base - self.exponent = exponent - self.precision = precision - - def _precise_year(self): - return int(self.base) * 10 ** int(self.exponent) - - def get_year(self): - if self.precision: - return f"{self.base}E{self.exponent}S{self.precision}" - else: - return f"{self.base}E{self.exponent}" - - year = property(get_year) diff --git a/vagrant wheel install problems.txt b/vagrant wheel install problems.txt deleted file mode 100644 index 174f67e..0000000 --- a/vagrant wheel install problems.txt +++ /dev/null @@ -1,5 +0,0 @@ -vagrant wheel install problems -https://stackoverflow.com/questions/56851961/how-to-fix-no-such-file-or-directory-error-in-setuptools-wheel-py157-convert - -from that link: -So it turns out that this problem was being caused by lag in Vagrant/Virtualbox's synced folders. I was trying to build the Python project inside a Vagrant VM shared from the host file system using a synced folder. Copying the project out of the synced folder into another folder in the VM allows it to build. Another dirty hack that worked was to add a time.sleep(1) in the setuptools/wheel.py source file on line 157 before the os.rename that was causing the OS Exception to be raised. This gives the file system a chance to sync, and therefore works around the issue. \ No newline at end of file