def second_minute(self, expr, prefix): """ sec/min expressions (n : Number, s: String) * nn (1~59) nn-nn nn/nn nn-nn/nn */nn nn,nn,nn (Maximum 24 elements) """ mi, mx = (0, 59) if re.fullmatch(r"\d{1,2}$", expr): self.check_range(expr=expr, mi=mi, mx=mx, prefix=prefix) elif re.search(r"[-*,/]", expr): if '*' == expr: pass elif re.fullmatch(r"\d{1,2}-\d{1,2}$", expr): parts = expr.split("-") self.check_range(expr=parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range(expr=parts[1], mi=mi, mx=mx, prefix=prefix) self.compare_range(st=parts[0], ed=parts[1], mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\d{1,2}/\d{1,2}$", expr): parts = expr.split("/") self.check_range(expr=parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range('interval', expr=parts[1], mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\d{1,2}-\d{1,2}/\d{1,2}$", expr): parts = expr.split("/") fst_parts = parts[0].split("-") self.check_range(expr=fst_parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range(expr=fst_parts[1], mi=mi, mx=mx, prefix=prefix) self.compare_range(st=fst_parts[0], ed=fst_parts[1], mi=mi, mx=mx, prefix=prefix) self.check_range('interval', expr=parts[1], mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\*/\d{1,2}$", expr): parts = expr.split("/") self.check_range('interval', expr=parts[1], mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"^\d{1,2}(,\d{1,2})+", expr): limit = 60 expr_ls = expr.split(",") if len(expr_ls) > limit: msg = "({0}) Exceeded maximum number({1}) of specified value. '{2}' is provided".format(prefix, limit, len( expr_ls)) raise FormatException(msg) else: for n in expr_ls: self.check_range(expr=n, mi=mi, mx=mx, prefix=prefix) else: msg = "({0}) Illegal Expression Format '{1}'".format(prefix, expr) raise FormatException(msg) else: msg = "({0}) Illegal Expression Format '{1}'".format(prefix, expr) raise FormatException(msg)
def year(self, expr, prefix): """ Year - valid expression (n : Number) * nnnn(1970~2099) - 4 digits number nnnn-nnnn(1970~2099) nnnn/nnn(0~129) */nnn(0~129) nnnn,nnnn,nnnn(1970~2099) - maximum 86 elements """ mi, mx = (1970, 2099) if re.fullmatch(r"\d{4}$", expr): self.check_range(expr=expr, mi=mi, mx=mx, prefix=prefix) elif re.search(r"[-*,/]", expr): if '*' == expr: pass elif re.fullmatch(r"\d{4}-\d{4}$", expr): parts = expr.split("-") self.check_range(expr=parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range(expr=parts[1], mi=mi, mx=mx, prefix=prefix) self.compare_range(st=parts[0], ed=parts[1], mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\d{4}/\d{1,3}$", expr): parts = expr.split("/") self.check_range(expr=parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range('interval', expr=parts[1], mi=0, mx=129, prefix=prefix) elif re.fullmatch(r"\d{4}-\d{4}/\d{1,3}$", expr): parts = expr.split("/") fst_parts = parts[0].split("-") self.check_range(expr=fst_parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range(expr=fst_parts[1], mi=mi, mx=mx, prefix=prefix) self.compare_range(st=fst_parts[0], ed=fst_parts[1], mi=mi, mx=mx, prefix=prefix) self.check_range('interval', expr=parts[1], mi=0, mx=129, prefix=prefix) elif re.fullmatch(r"\*/\d{1,3}$", expr): parts = expr.split("/") self.check_range('interval', expr=parts[1], mi=0, mx=129, prefix=prefix) elif re.fullmatch(r"^\d{4}(,\d{4})+", expr): limit = 84 expr_ls = expr.split(",") if len(expr_ls) > limit: msg = "({0}) Exceeded maximum number({1}) of specified value. '{2}' is provided".format(prefix, limit, len( expr_ls)) raise FormatException(msg) else: for year in expr_ls: self.check_range(expr=year, mi=mi, mx=mx, prefix=prefix) else: msg = "({0}) Illegal Expression Format '{1}'".format(prefix, expr) raise FormatException(msg) else: msg = "({0}) Illegal Expression Format '{1}'".format(prefix, expr) raise FormatException(msg)
def parse(self): """Parses the cron expression string Returns: A 7 part string array, one part for each component of the cron expression (seconds, minutes, etc.) Raises: MissingFieldException: if _expression is empty or None FormatException: if _expression has wrong format """ # Initialize all elements of parsed array to empty strings parsed = ['', '', '', '', '', '', ''] if self._expression is None or len(self._expression) == 0: raise MissingFieldException("ExpressionDescriptor.expression") else: expression_parts_temp = self._expression.split() expression_parts_temp_length = len(expression_parts_temp) if expression_parts_temp_length < 5: raise FormatException( "Error: Expression only has {0} parts. At least 5 part are required." .format(expression_parts_temp_length)) elif expression_parts_temp_length == 5: # 5 part cron so shift array past seconds element for i, expression_part_temp in enumerate( expression_parts_temp): parsed[i + 1] = expression_part_temp elif expression_parts_temp_length == 6: # If last element ends with 4 digits, a year element has been # supplied and no seconds element year_regex = re.compile("\d{4}$") if year_regex.search(expression_parts_temp[5]) is not None: for i, expression_part_temp in enumerate( expression_parts_temp): parsed[i + 1] = expression_part_temp else: for i, expression_part_temp in enumerate( expression_parts_temp): parsed[i] = expression_part_temp elif expression_parts_temp_length == 7: parsed = expression_parts_temp else: raise FormatException( "Error: Expression has too many parts ({0}). Expression must not have more than 7 parts." .format(expression_parts_temp_length)) self.normalize_expression(parsed) return parsed
def compare_range(self, type=None, **kwargs): """ check 2 expression values size does not allow {st} value to be greater than {ed} value """ prefix = kwargs["prefix"] st = kwargs["st"] ed = kwargs["ed"] mi = kwargs["mi"] mx = kwargs["mx"] if int(st) > int(ed): if type is None: msg = "({0}) Invalid range '{1}-{2}'. Accepted range is {3}-{4}".format(prefix, st, ed, mi, mx) elif type == 'dow': msg = "({0}) Invalid range '{1}-{2}'. Accepted range is {3}-{4}".format(prefix, self._cron_days[st], self._cron_days[ed], mi, mx) raise FormatException(msg)
def check_range(self, type=None, **kwargs): """ check if expression value within range of specified limit """ prefix = kwargs["prefix"] mi = kwargs["mi"] mx = kwargs["mx"] expr = kwargs["expr"] if int(expr) < mi or mx < int(expr): if type is None: msg = "{0} values must be between {1} and {2} but '{3}' is provided".format(prefix, mi, mx, expr) elif type == "interval": msg = "({0}) Accepted increment value range is {1}~{2} but '{3}' is provided".format(prefix, mi, mx, expr) elif type == 'dow': msg = "({0}) Accepted week value is {1}~{2} but '{3}' is provided".format(prefix, mi, mx, expr) raise FormatException(msg) else: pass
def dayofweek(self, expr, prefix): """ DAYOfWeek expressions (n : Number, s: String) * ? n (0~7) - 0 and 7 used interchangeable as Sunday sss (SUN~SAT) n/n n-n/n */n n-n sss-sss n|sss,n|sss,n|sss (maximum 7 elements) nL n#n """ mi, mx = (0, 7) if '*' == expr: pass elif '?' == expr: pass elif re.fullmatch(r"\d{1}$", expr): self.check_range(expr=expr, mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\D{3}", expr): cron_days = {v: k for (k, v) in self._cron_days.items()} if expr.upper() in cron_days: pass else: msg = "Invalid DayOfWeek value '{}'".format(expr) raise FormatException(msg) elif re.fullmatch(r"\d{1}/\d{1}$", expr): parts = expr.split("/") self.check_range(expr=parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range('interval', expr=parts[1], mi=0, mx=mx, prefix=prefix) elif re.fullmatch(r"\d{1}-\d{1}/\d{1}$", expr): parts = expr.split("/") fst_parts = parts[0].split("-") self.check_range(expr=fst_parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range(expr=fst_parts[1], mi=mi, mx=mx, prefix=prefix) self.compare_range(st=fst_parts[0], ed=fst_parts[1], mi=mi, mx=mx, prefix=prefix) self.check_range('interval', expr=parts[1], mi=0, mx=mx, prefix=prefix) elif re.fullmatch(r"[*]/\d{1}$", expr): parts = expr.split("/") self.check_range('interval', expr=parts[1], mi=0, mx=mx, prefix=prefix) elif re.fullmatch(r"\d{1}-\d{1}$", expr): parts = expr.split("-") self.check_range(expr=parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range(expr=parts[1], mi=mi, mx=mx, prefix=prefix) self.compare_range(st=parts[0], ed=parts[1], mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\D{3}-\D{3}$", expr): parts = expr.split("-") cron_days = {v: k for (k, v) in self._cron_days.items()} try: st_day = cron_days[parts[0].upper()] ed_day = cron_days[parts[1].upper()] except KeyError: msg = "({0}) Invalid DayOfWeek value '{1}'".format(prefix, expr) raise FormatException(msg) self.compare_range(st=st_day, ed=ed_day, mi=mi, mx=mx, prefix=prefix, type='dow') elif re.fullmatch(r"^(\d{1}|\D{3})((,\d{1})+|(,\D{3})*)*", expr): limit = 7 expr_ls = expr.split(",") if len(expr_ls) > limit: msg = "({0}) Exceeded maximum number({1}) of specified value. '{2}' is provided".format(prefix, limit, len(expr_ls)) raise FormatException(msg) else: cron_days = {v: k for (k, v) in self._cron_days.items()} for day in expr_ls: day = cron_days[day.upper()] + 1 if len(day) == 3 else day # syncronize by add 1 to cron_days index self.check_range(expr=day, mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\d{1}(l|L)", expr): self.check_range(expr=expr[0], mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\d{1}#\d{1}", expr): parts = expr.split('#') self.check_range(expr=parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range(expr=parts[1], mi=mi, mx=5, prefix=prefix, type='dow') else: msg = "({0}) Illegal Expression Format '{1}'".format(prefix, expr) raise FormatException(msg)
def month(self, expr, prefix): """ month expressions (n : Number, s: String) * nn (1~12) sss (JAN~DEC) nn-nn sss-sss nn/nn nn-nn/nn */nn nn,nn,nn (Maximum 12 elements) """ mi, mx = (1, 12) if re.fullmatch(r"\d{1,2}$", expr): self.check_range(expr=expr, mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\D{3}", expr): matched_month = [m for m in self._cron_months.values() if expr == m] if len(matched_month) == 0: msg = "Invalid Month value '{}'".format(expr) raise FormatException(msg) elif re.search(r"[-*,/]", expr): if '*' == expr: pass elif re.fullmatch(r"\d{1,2}-\d{1,2}$", expr): parts = expr.split("-") self.check_range(expr=parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range(expr=parts[1], mi=mi, mx=mx, prefix=prefix) self.compare_range(st=parts[0], ed=parts[1], mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\D{3}-\D{3}$", expr): parts = expr.split("-") cron_months = {v: k for (k, v) in self._cron_months.items()} st_not_exist = parts[0] not in cron_months ed_not_exist = parts[1] not in cron_months if st_not_exist or ed_not_exist: msg = "Invalid Month value '{}'".format(expr) raise FormatException(msg) self.compare_range(st=cron_months[parts[0]], ed=cron_months[parts[1]], mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\d{1,2}/\d{1,2}$", expr): parts = expr.split("/") self.check_range(expr=parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range('interval', expr=parts[1], mi=0, mx=mx, prefix=prefix) elif re.fullmatch(r"\d{1,2}-\d{1,2}/\d{1,2}$", expr): parts = expr.split("/") fst_parts = parts[0].split("-") self.check_range(expr=fst_parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range(expr=fst_parts[1], mi=mi, mx=mx, prefix=prefix) self.compare_range(st=fst_parts[0], ed=fst_parts[1], mi=mi, mx=mx, prefix=prefix) self.check_range('interval', expr=parts[1], mi=0, mx=12, prefix=prefix) elif re.fullmatch(r"\*/\d{1,2}$", expr): parts = expr.split("/") self.check_range('interval', expr=parts[1], mi=0, mx=12, prefix=prefix) elif re.fullmatch(r"^\d{1,2}(,\d{1,2})+", expr): limit = 12 expr_ls = expr.split(",") if len(expr_ls) > limit: msg = "({0}) Exceeded maximum number({1}) of specified value. '{2}' is provided".format(prefix, limit, len( expr_ls)) raise FormatException(msg) else: for month in expr_ls: self.check_range(expr=month, mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"^(\d{1,2}|\D{3})((,\d{1,2})+|(,\D{3})*)*", expr): limit = 12 expr_ls = expr.split(",") if len(expr_ls) > limit: msg = "({0}) Exceeded maximum number({1}) of specified value. '{2}' is provided".format(prefix, limit, len( expr_ls)) raise FormatException(msg) else: cron_months = {v: k for (k, v) in self._cron_months.items()} for month in expr_ls: month = cron_months[month.upper()] if len(month) == 3 else month self.check_range(expr=month, mi=mi, mx=mx, prefix=prefix) else: msg = "({0}) Illegal Expression Format '{1}'".format(prefix, expr) raise FormatException(msg) else: msg = "({0}) Illegal Expression Format '{1}'".format(prefix, expr) raise FormatException(msg)
def dayofmonth(self, expr, prefix): """ DAYOfMonth expressions (n : Number, s: String) * ? nn (1~31) nn-nn nn/nn nn-nn/nn */nn nn,nn,nn (Maximum 31 elements) L-nn LW nW """ mi, mx = (1, 31) if re.fullmatch(r"\d{1,2}$", expr): self.check_range(expr=expr, mi=mi, mx=mx, prefix=prefix) elif re.search(r"[-*,/?]", expr): if '*' == expr: pass elif '?' == expr: pass elif re.fullmatch(r"\d{1,2}-\d{1,2}$", expr): parts = expr.split("-") self.check_range(expr=parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range(expr=parts[1], mi=mi, mx=mx, prefix=prefix) self.compare_range(st=parts[0], ed=parts[1], mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"\d{1,2}/\d{1,2}$", expr): parts = expr.split("/") self.check_range(expr=parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range('interval', expr=parts[1], mi=0, mx=mx, prefix=prefix) elif re.fullmatch(r"\d{1,2}-\d{1,2}/\d{1,2}$", expr): parts = expr.split("/") fst_parts = parts[0].split("-") self.check_range(expr=fst_parts[0], mi=mi, mx=mx, prefix=prefix) self.check_range(expr=fst_parts[1], mi=mi, mx=mx, prefix=prefix) self.compare_range(st=fst_parts[0], ed=fst_parts[1], mi=mi, mx=mx, prefix=prefix) self.check_range('interval', expr=parts[1], mi=0, mx=mx, prefix=prefix) elif re.fullmatch(r"\*/\d{1,2}$", expr): parts = expr.split("/") self.check_range('interval', expr=parts[1], mi=0, mx=mx, prefix=prefix) elif re.fullmatch(r"^\d{1,2}(,\d{1,2})+", expr): limit = 31 expr_ls = expr.split(",") if len(expr_ls) > 31: msg = "({0}) Exceeded maximum number({1}) of specified value. '{2}' is provided".format(prefix, limit, len( expr_ls)) raise FormatException(msg) else: for dayofmonth in expr_ls: self.check_range(expr=dayofmonth, mi=mi, mx=mx, prefix=prefix) elif re.fullmatch(r"^(L|l)-(\d{1,2})$", expr): parts = expr.split("-") self.check_range(expr=parts[1], mi=mi, mx=mx, prefix=prefix) else: msg = "Illegal Expression Format '{0}'".format(expr) raise FormatException(msg) elif re.fullmatch(r"^(L|l)(W|w)?$", expr): pass elif re.fullmatch(r"^(\d{1,2})(w{1}|W{1})$", expr): self.check_range(expr=expr[:-1], mi=mi, mx=mx, prefix=prefix) else: msg = "({0}) Illegal Expression Format '{1}'".format(prefix, expr) raise FormatException(msg)