def coerce_xtrigger(self, value, keys): """Coerce a string into an xtrigger function context object. func_name(*func_args, **func_kwargs) Checks for legal string templates in arg values too. """ label = keys[-1] value = self.strip_and_unquote(keys, value) if not value: raise IllegalValueError("xtrigger", keys, value) fname = None args = [] kwargs = {} match = self._REC_TRIG_FUNC.match(value) if match is None: raise IllegalValueError("xtrigger", keys, value) fname, fargs, intvl = match.groups() if intvl: intvl = self.coerce_interval(intvl, keys) if fargs: # Extract function args and kwargs. for farg in fargs.split(r','): try: key, val = farg.strip().split(r'=', 1) except ValueError: args.append(self._coerce_type(farg.strip())) else: kwargs[key.strip()] = self._coerce_type(val.strip()) return SubFuncContext(label, fname, args, kwargs, intvl)
def coerce_cycle_point_format(cls, value, keys): """Coerce to a cycle point format (either CCYYMM... or %Y%m...).""" value = cls.strip_and_unquote(keys, value) if not value: return None test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) if '/' in value: raise IllegalValueError('cycle point format', keys, value) if '%' in value: try: TimePointDumper().strftime(test_timepoint, value) except ValueError: raise IllegalValueError('cycle point format', keys, value) return value if 'X' in value: for i in range(1, 101): dumper = TimePointDumper(num_expanded_year_digits=i) try: dumper.dump(test_timepoint, value) except ValueError: continue return value raise IllegalValueError('cycle point format', keys, value) dumper = TimePointDumper() try: dumper.dump(test_timepoint, value) except ValueError: raise IllegalValueError('cycle point format', keys, value) return value
def coerce_cycle_point(cls, value, keys): """Coerce value to a cycle point.""" if not value: return None value = cls.strip_and_unquote(keys, value) if value == 'now': # Handle this later in config.py when the suite UTC mode is known. return value if value.isdigit(): # Could be an old date-time cycle point format, or integer format. return value if value.startswith('-') or value.startswith('+'): # We don't know the value given for num expanded year digits... for i in range(1, 101): try: TimePointParser(num_expanded_year_digits=i).parse(value) except ValueError: continue return value raise IllegalValueError('cycle point', keys, value) try: TimePointParser().parse(value) except ValueError: raise IllegalValueError('cycle point', keys, value) return value
def _coerce_cycletime(value, keys, _): """Coerce value to a cycle point.""" if not value: return None value = _strip_and_unquote(keys, value) if value == "now": # Handle this later in config.py when the suite UTC mode is known. return value if re.match(r"\d+$", value): # Could be an old date-time cycle point format, or integer format. return value if value.startswith("-") or value.startswith("+"): # We don't know the value given for num expanded year digits... for i in range(1, 101): parser = TimePointParser(num_expanded_year_digits=i) try: parser.parse(value) except ValueError: continue return value raise IllegalValueError("cycle point", keys, value) parser = TimePointParser() try: parser.parse(value) except ValueError: raise IllegalValueError("cycle point", keys, value) return value
def _coerce_cycletime_time_zone(value, keys, _): """Coerce value to a cycle point time zone format - Z, +13, -0800...""" value = _strip_and_unquote(keys, value) if not value: return None try: set_syntax_version(VERSION_NEW, "use of [cylc]cycle point time zone format") except SyntaxVersionError: raise IllegalValueError("cycle point time zone format", keys, value) test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) dumper = TimePointDumper() test_timepoint_string = dumper.dump(test_timepoint, "CCYYMMDDThhmmss") test_timepoint_string += value parser = TimePointParser(allow_only_basic=True) try: parser.parse(test_timepoint_string) except ValueError: raise IllegalValueError("cycle point time zone format", keys, value) return value
def coerce_xtrig(value, keys, _): """Coerce a string into an xtrigger function context object. func_name(*func_args, **func_kwargs) Checks for legal string templates in arg values too. """ def coerce_type(in_str): """Convert in_str to int, float, or bool, if possible.""" try: val = int(in_str) except ValueError: try: val = float(in_str) except ValueError: if in_str == 'False': val = False elif in_str == 'True': val = True else: # Leave as string. val = _strip_and_unquote([], in_str) return val label = keys[-1] value = _strip_and_unquote(keys, value) if not value: raise IllegalValueError("xtrigger", keys, value) fname = None args = [] kwargs = {} m = RE_TRIG_FUNC.match(value) if m is None: raise IllegalValueError("xtrigger", keys, value) fname, fargs, intvl = m.groups() if intvl is None: seconds = DEFAULT_XTRIG_INTVL_SECS else: seconds = float(coerce_interval(intvl, keys, None)) if fargs: # Extract function args and kwargs. for farg in re.split(r'\s*,\s*', fargs): try: key, val = re.split(r'\s*=\s*', farg) except ValueError: args.append(coerce_type(farg.strip())) else: kwargs[key.strip()] = coerce_type(val) return SuiteFuncContext(label, fname, args, kwargs, seconds)
def coerce_interval(value, keys, _, back_comp_unit_factor=1, check_syntax_version=True): """Coerce an ISO 8601 interval (or number: back-comp) into seconds.""" value = _strip_and_unquote(keys, value) if not value: # Allow explicit empty values. return None try: backwards_compat_value = float(value) * back_comp_unit_factor except (TypeError, ValueError): pass else: if check_syntax_version: set_syntax_version( VERSION_PREV, "integer interval: %s" % itemstr(keys[:-1], keys[-1], value)) return DurationFloat(backwards_compat_value) try: interval = DURATION_PARSER.parse(value) except ValueError: raise IllegalValueError("ISO 8601 interval", keys, value) if check_syntax_version: set_syntax_version( VERSION_NEW, "ISO 8601 interval: %s" % itemstr(keys[:-1], keys[-1], value)) days, seconds = interval.get_days_and_seconds() return DurationFloat(days * CALENDAR.SECONDS_IN_DAY + seconds)
def coerce_interval(value, keys, args, back_comp_unit_factor=1, check_syntax_version=True): """Coerce an ISO 8601 interval (or number: back-comp) into seconds.""" value = _strip_and_unquote(keys, value) try: backwards_compat_value = float(value) * back_comp_unit_factor except (TypeError, ValueError): pass else: if check_syntax_version: set_syntax_version( VERSION_PREV, "integer interval: %s" % itemstr(keys[:-1], keys[-1], value)) return backwards_compat_value try: interval = interval_parser.parse(value) except ValueError: raise IllegalValueError("ISO 8601 interval", keys, value) if check_syntax_version: try: set_syntax_version( VERSION_NEW, "ISO 8601 interval: %s" % itemstr(keys[:-1], keys[-1], value)) except SyntaxVersionError as exc: raise Exception(str(exc)) days, seconds = interval.get_days_and_seconds() seconds += days * Calendar.default().SECONDS_IN_DAY return seconds
def _coerce_cycleinterval(value, keys, _): """Coerce value to a cycle interval.""" if not value: return None value = _strip_and_unquote(keys, value) if value.isdigit(): # Old runahead limit format. set_syntax_version( VERSION_PREV, "integer interval for %s" % itemstr(keys[:-1], keys[-1], value)) return value if REC_INTEGER_INTERVAL.match(value): # New integer cycling format. set_syntax_version( VERSION_NEW, "integer interval for %s" % itemstr(keys[:-1], keys[-1], value)) return value parser = DurationParser() try: parser.parse(value) except ValueError: raise IllegalValueError("interval", keys, value) set_syntax_version( VERSION_NEW, "ISO 8601 interval for %s" % itemstr(keys[:-1], keys[-1], value)) return value
def coerce_parameter_list(cls, value, keys): """Coerce parameter list. Args: value (str): This can be a list of str values. Each str value must conform to the same restriction as a task name. Otherwise, this can be a mixture of int ranges and int values. keys (list): Keys in nested dict that represents the raw configuration. Return (list): A list of strings or a list of sorted integers. Raise: IllegalValueError: If value has both str and int range or if a str value breaks the task name restriction. """ items = [] can_only_be = None # A flag to prevent mixing str and int range for item in cls.strip_and_unquote_list(keys, value): values = cls.parse_int_range(item) if values is not None: if can_only_be == str: raise IllegalValueError('parameter', keys, value, 'mixing int range and str') can_only_be = int items.extend(values) elif cls._REC_NAME_SUFFIX.match(item): try: int(item) except ValueError: if can_only_be == int: raise IllegalValueError('parameter', keys, value, 'mixing int range and str') can_only_be = str items.append(item) else: raise IllegalValueError('parameter', keys, value, '%s: bad value' % item) try: return [int(item) for item in items] except ValueError: return items
def get_interval_as_seconds(intvl, keys=None): """Convert an ISO 8601 interval to seconds.""" if keys is None: keys = [] try: interval = DURATION_PARSER.parse(intvl) except ValueError: raise IllegalValueError("ISO 8601 interval", keys, intvl) days, seconds = interval.get_days_and_seconds() return days * CALENDAR.SECONDS_IN_DAY + seconds
def _coerce_cycleinterval(value, keys, _): """Coerce value to a cycle interval.""" if not value: return None value = _strip_and_unquote(keys, value) parser = DurationParser() try: parser.parse(value) except ValueError: raise IllegalValueError("interval", keys, value) return value
def _coerce_parameter_list(value, keys, _): """Coerce parameter list Can be: * A list of str values. Each str value must conform to the same restriction as a task name. Return list of str values. * A mixture of int ranges and int values. Return list of str values containing the sorted int list, zero-padded to the same width. Raise IllegalValueError if: * Mixing str and int range. * A str value breaks the task name restriction. """ items = [] can_only_be = None # A flag to prevent mixing str and int range for item in _strip_and_unquote_list(keys, value): match = REC_PARAM_INT_RANGE.match(item) if match: if can_only_be == str: raise IllegalValueError('parameter', keys, value, 'mixing int range and str') can_only_be = int lower, upper, step = match.groups() if not step: step = 1 items.extend(range(int(lower), int(upper) + 1, int(step))) elif TaskID.NAME_SUFFIX_REC.match(item): if not item.isdigit(): if can_only_be == int: raise IllegalValueError('parameter', keys, value, 'mixing int range and str') can_only_be = str items.append(item) else: raise IllegalValueError('parameter', keys, value, '%s: bad value' % item) if not items or can_only_be == str or any(not str(item).isdigit() for item in items): return items else: return [int(item) for item in items]
def coerce_cycle_point(cls, value, keys): """Coerce value to a cycle point.""" if not value: return None value = cls.strip_and_unquote(keys, value) if value == 'now': # Handle this later in config.py when the suite UTC mode is known. return value if "next" in value or "previous" in value: # Handle this later, as for "now". return value if value.isdigit(): # Could be an old date-time cycle point format, or integer format. return value if "P" not in value and ( value.startswith('-') or value.startswith('+')): # We don't know the value given for num expanded year digits... for i in range(1, 101): try: TimePointParser(num_expanded_year_digits=i).parse(value) except ValueError: continue return value raise IllegalValueError('cycle point', keys, value) if "P" in value: # ICP is an offset parser = DurationParser() try: if value.startswith("-"): # parser doesn't allow negative duration with this setup? parser.parse(value[1:]) else: parser.parse(value) return value except ValueError: raise IllegalValueError("cycle point", keys, value) try: TimePointParser().parse(value) except ValueError: raise IllegalValueError('cycle point', keys, value) return value
def coerce_interval(value, keys, _): """Coerce an ISO 8601 interval (or number: back-comp) into seconds.""" value = _strip_and_unquote(keys, value) if not value: # Allow explicit empty values. return None try: interval = DURATION_PARSER.parse(value) except ValueError: raise IllegalValueError("ISO 8601 interval", keys, value) days, seconds = interval.get_days_and_seconds() return DurationFloat(days * CALENDAR.SECONDS_IN_DAY + seconds)
def coerce_interval(self, value, keys): """Coerce an ISO 8601 interval (or number: back-comp) into seconds.""" value = self.strip_and_unquote(keys, value) if not value: # Allow explicit empty values. return None try: interval = DurationParser().parse(value) except ValueError: raise IllegalValueError("ISO 8601 interval", keys, value) days, seconds = interval.get_days_and_seconds() return DurationFloat(days * Calendar.default().SECONDS_IN_DAY + seconds)
def init(num_expanded_year_digits=0, custom_dump_format=None, time_zone=None, assume_utc=False, cycling_mode=None): """Initialise suite-setup-specific information.""" SuiteSpecifics.interval_parser = DurationParser() if cycling_mode in Calendar.default().MODES: Calendar.default().set_mode(cycling_mode) if time_zone is None: if assume_utc: time_zone = "Z" time_zone_hours_minutes = (0, 0) else: time_zone = get_local_time_zone_format(reduced_mode=True) time_zone_hours_minutes = get_local_time_zone() else: time_zone_hours_minutes = TimePointDumper().get_time_zone(time_zone) SuiteSpecifics.ASSUMED_TIME_ZONE = time_zone_hours_minutes SuiteSpecifics.NUM_EXPANDED_YEAR_DIGITS = num_expanded_year_digits if custom_dump_format is None: if num_expanded_year_digits > 0: SuiteSpecifics.DUMP_FORMAT = EXPANDED_DATE_TIME_FORMAT + time_zone else: SuiteSpecifics.DUMP_FORMAT = DATE_TIME_FORMAT + time_zone else: SuiteSpecifics.DUMP_FORMAT = custom_dump_format if u"+X" not in custom_dump_format and num_expanded_year_digits: raise IllegalValueError('cycle point format', ('cylc', 'cycle point format'), SuiteSpecifics.DUMP_FORMAT) SuiteSpecifics.point_parser = TimePointParser( allow_only_basic=False, allow_truncated=True, num_expanded_year_digits=SuiteSpecifics.NUM_EXPANDED_YEAR_DIGITS, dump_format=SuiteSpecifics.DUMP_FORMAT, assumed_time_zone=time_zone_hours_minutes) custom_point_parse_function = None if SuiteSpecifics.DUMP_FORMAT == PREV_DATE_TIME_FORMAT: custom_point_parse_function = point_parse SuiteSpecifics.abbrev_util = CylcTimeParser( None, None, num_expanded_year_digits=SuiteSpecifics.NUM_EXPANDED_YEAR_DIGITS, dump_format=SuiteSpecifics.DUMP_FORMAT, custom_point_parse_function=custom_point_parse_function, assumed_time_zone=SuiteSpecifics.ASSUMED_TIME_ZONE)
def _coerce_cycletime_format(value, keys, _): """Coerce value to a cycle point format (either CCYYMM... or %Y%m...).""" value = _strip_and_unquote(keys, value) if not value: return None try: set_syntax_version(VERSION_NEW, "use of [cylc]cycle point format") except SyntaxVersionError: raise IllegalValueError("cycle point format", keys, value) test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) if "/" in value: raise IllegalValueError("cycle point format", keys, value) if "%" in value: try: TimePointDumper().strftime(test_timepoint, value) except ValueError: raise IllegalValueError("cycle point format", keys, value) return value if "X" in value: for i in range(1, 101): dumper = TimePointDumper(num_expanded_year_digits=i) try: dumper.dump(test_timepoint, value) except ValueError: continue return value raise IllegalValueError("cycle point format", keys, value) dumper = TimePointDumper() try: dumper.dump(test_timepoint, value) except ValueError: raise IllegalValueError("cycle point format", keys, value) return value
def _coerce_cycletime(value, keys, args): """Coerce value to a cycle point.""" value = _strip_and_unquote(keys, value) if re.match(r"\d+$", value): # Could be an old date-time cycle point format, or integer format. return value if value.startswith("-") or value.startswith("+"): # We don't know the value given for num expanded year digits... for i in range(1, 101): parser = TimePointParser(num_expanded_year_digits=i) try: parser.parse(value) except ValueError: continue return value raise IllegalValueError("cycle point", keys, value) parser = TimePointParser() try: parser.parse(value) except ValueError: raise IllegalValueError("cycle point", keys, value) set_syntax_version(VERSION_NEW, "cycle point: %s" % itemstr(keys[:-1], keys[-1], value)) return value
def coerce_cycle_point_time_zone(cls, value, keys): """Coerce value to a cycle point time zone format - Z, +13, -0800...""" value = cls.strip_and_unquote(keys, value) if not value: return None test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) dumper = TimePointDumper() test_timepoint_string = dumper.dump(test_timepoint, 'CCYYMMDDThhmmss') test_timepoint_string += value parser = TimePointParser(allow_only_basic=True) try: parser.parse(test_timepoint_string) except ValueError: raise IllegalValueError( 'cycle point time zone format', keys, value) return value
def _coerce_parameter_list(value, keys, _): """Coerce parameter list.""" value = _strip_and_unquote_list(keys, value) if len(value) == 1: # May be a range e.g. '1..5' (bounds inclusive) try: lower, upper = REC_PARAM_INT_RANGE.match(value[0]).groups() except AttributeError: if '.' in value[0]: # Dot is illegal in node names, probably bad range syntax. raise IllegalValueError("parameter", keys, value) else: n_dig = len(upper) return [ str(i).zfill(n_dig) for i in range(int(lower), int(upper) + 1) ] return value
def init(num_expanded_year_digits=0, custom_dump_format=None, time_zone=None, assume_utc=False, cycling_mode=None): """Initialise suite-setup-specific information.""" if cycling_mode in Calendar.default().MODES: Calendar.default().set_mode(cycling_mode) if time_zone is None: if assume_utc: time_zone = "Z" time_zone_hours_minutes = (0, 0) else: time_zone = get_local_time_zone_format(TimeZoneFormatMode.reduced) time_zone_hours_minutes = get_local_time_zone() else: time_zone_hours_minutes = TimePointDumper().get_time_zone(time_zone) SuiteSpecifics.ASSUMED_TIME_ZONE = time_zone_hours_minutes SuiteSpecifics.NUM_EXPANDED_YEAR_DIGITS = num_expanded_year_digits if custom_dump_format is None: if num_expanded_year_digits > 0: SuiteSpecifics.DUMP_FORMAT = EXPANDED_DATE_TIME_FORMAT + time_zone else: SuiteSpecifics.DUMP_FORMAT = DATE_TIME_FORMAT + time_zone else: SuiteSpecifics.DUMP_FORMAT = custom_dump_format if u"+X" not in custom_dump_format and num_expanded_year_digits: raise IllegalValueError( 'cycle point format', ('cylc', 'cycle point format'), SuiteSpecifics.DUMP_FORMAT ) SuiteSpecifics.iso8601_parsers = CylcTimeParser.initiate_parsers( dump_format=SuiteSpecifics.DUMP_FORMAT, num_expanded_year_digits=num_expanded_year_digits, assumed_time_zone=SuiteSpecifics.ASSUMED_TIME_ZONE ) (SuiteSpecifics.point_parser, SuiteSpecifics.interval_parser, SuiteSpecifics.recurrence_parser) = SuiteSpecifics.iso8601_parsers SuiteSpecifics.abbrev_util = CylcTimeParser( None, None, SuiteSpecifics.iso8601_parsers )