| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- # Copyright (c) 2010-2024 openpyxl
- import re
- from openpyxl.descriptors import (
- Strict,
- Integer,
- String,
- Typed,
- )
- from openpyxl.utils import quote_sheetname, absolute_coordinate
- from openpyxl.utils.cell import SHEET_TITLE, SHEETRANGE_RE, RANGE_EXPR
- from .cell_range import MultiCellRange
- COL_RANGE = r"""(?P<cols>[$]?(?P<min_col>[a-zA-Z]{1,3}):[$]?(?P<max_col>[a-zA-Z]{1,3}))"""
- COL_RANGE_RE = re.compile(COL_RANGE)
- ROW_RANGE = r"""(?P<rows>[$]?(?P<min_row>\d+):[$]?(?P<max_row>\d+))"""
- ROW_RANGE_RE = re.compile(ROW_RANGE)
- TITLES_REGEX = re.compile("""{0}{1}?,?{2}?,?""".format(SHEET_TITLE, ROW_RANGE, COL_RANGE),
- re.VERBOSE)
- PRINT_AREA_RE = re.compile(f"({SHEET_TITLE})?(?P<cells>{RANGE_EXPR})", re.VERBOSE)
- class ColRange(Strict):
- """
- Represent a range of at least one column
- """
- min_col = String()
- max_col = String()
- def __init__(self, range_string=None, min_col=None, max_col=None):
- if range_string is not None:
- match = COL_RANGE_RE.match(range_string)
- if not match:
- raise ValueError(f"{range_string} is not a valid column range")
- min_col, max_col = match.groups()[1:]
- self.min_col = min_col
- self.max_col = max_col
- def __eq__(self, other):
- if isinstance(other, self.__class__):
- return (self.min_col == other.min_col
- and
- self.max_col == other.max_col)
- elif isinstance(other, str):
- return (str(self) == other
- or
- f"{self.min_col}:{self.max_col}")
- return False
- def __repr__(self):
- return f"Range of columns from '{self.min_col}' to '{self.max_col}'"
- def __str__(self):
- return f"${self.min_col}:${self.max_col}"
- class RowRange(Strict):
- """
- Represent a range of at least one row
- """
- min_row = Integer()
- max_row = Integer()
- def __init__(self, range_string=None, min_row=None, max_row=None):
- if range_string is not None:
- match = ROW_RANGE_RE.match(range_string)
- if not match:
- raise ValueError(f"{range_string} is not a valid row range")
- min_row, max_row = match.groups()[1:]
- self.min_row = min_row
- self.max_row = max_row
- def __eq__(self, other):
- if isinstance(other, self.__class__):
- return (self.min_row == other.min_row
- and
- self.max_row == other.max_row)
- elif isinstance(other, str):
- return (str(self) == other
- or
- f"{self.min_row}:{self.max_row}")
- return False
- def __repr__(self):
- return f"Range of rows from '{self.min_row}' to '{self.max_row}'"
- def __str__(self):
- return f"${self.min_row}:${self.max_row}"
- class PrintTitles(Strict):
- """
- Contains at least either a range of rows or columns
- """
- cols = Typed(expected_type=ColRange, allow_none=True)
- rows = Typed(expected_type=RowRange, allow_none=True)
- title = String()
- def __init__(self, cols=None, rows=None, title=""):
- self.cols = cols
- self.rows = rows
- self.title = title
- @classmethod
- def from_string(cls, value):
- kw = dict((k, v) for match in TITLES_REGEX.finditer(value)
- for k, v in match.groupdict().items() if v)
- if not kw:
- raise ValueError(f"{value} is not a valid print titles definition")
- cols = rows = None
- if "cols" in kw:
- cols = ColRange(kw["cols"])
- if "rows" in kw:
- rows = RowRange(kw["rows"])
- title = kw.get("quoted") or kw.get("notquoted")
- return cls(cols=cols, rows=rows, title=title)
- def __eq__(self, other):
- if isinstance(other, self.__class__):
- return (self.cols == other.cols
- and
- self.rows == other.rows
- and
- self.title == other.title)
- elif isinstance(other, str):
- return str(self) == other
- return False
- def __repr__(self):
- return f"Print titles for sheet {self.title} cols {self.rows}, rows {self.cols}"
- def __str__(self):
- title = quote_sheetname(self.title)
- titles = ",".join([f"{title}!{value}" for value in (self.rows, self.cols) if value])
- return titles or ""
- class PrintArea(MultiCellRange):
- @classmethod
- def from_string(cls, value):
- new = []
- for m in PRINT_AREA_RE.finditer(value): # can be multiple
- coord = m.group("cells")
- if coord:
- new.append(coord)
- return cls(new)
- def __init__(self, ranges=(), title=""):
- self.title = ""
- super().__init__(ranges)
- def __str__(self):
- if self.ranges:
- return ",".join([f"{quote_sheetname(self.title)}!{absolute_coordinate(str(range))}"
- for range in self.sorted()])
- return ""
- def __eq__(self, other):
- super().__eq__(other)
- if isinstance(other, str):
- return str(self) == other
|