1# Copyright (c) 2010-2024 openpyxl
2
3from itertools import chain
4
5from openpyxl.descriptors.serialisable import Serialisable
6from openpyxl.descriptors import (
7 MinMax,
8 Typed,
9 String,
10 Strict,
11)
12from openpyxl.worksheet.worksheet import Worksheet
13from openpyxl.utils import (
14 get_column_letter,
15 range_to_tuple,
16 quote_sheetname
17)
18
19
20class DummyWorksheet:
21
22
23 def __init__(self, title):
24 self.title = title
25
26
27class Reference(Strict):
28
29 """
30 Normalise cell range references
31 """
32
33 min_row = MinMax(min=1, max=1000000, expected_type=int)
34 max_row = MinMax(min=1, max=1000000, expected_type=int)
35 min_col = MinMax(min=1, max=16384, expected_type=int)
36 max_col = MinMax(min=1, max=16384, expected_type=int)
37 range_string = String(allow_none=True)
38
39 def __init__(self,
40 worksheet=None,
41 min_col=None,
42 min_row=None,
43 max_col=None,
44 max_row=None,
45 range_string=None
46 ):
47 if range_string is not None:
48 sheetname, boundaries = range_to_tuple(range_string)
49 min_col, min_row, max_col, max_row = boundaries
50 worksheet = DummyWorksheet(sheetname)
51
52 self.worksheet = worksheet
53 self.min_col = min_col
54 self.min_row = min_row
55 if max_col is None:
56 max_col = min_col
57 self.max_col = max_col
58 if max_row is None:
59 max_row = min_row
60 self.max_row = max_row
61
62
63 def __repr__(self):
64 return str(self)
65
66
67 def __str__(self):
68 fmt = u"{0}!${1}${2}:${3}${4}"
69 if (self.min_col == self.max_col
70 and self.min_row == self.max_row):
71 fmt = u"{0}!${1}${2}"
72 return fmt.format(self.sheetname,
73 get_column_letter(self.min_col), self.min_row,
74 get_column_letter(self.max_col), self.max_row
75 )
76
77
78 __str__ = __str__
79
80
81
82 def __len__(self):
83 if self.min_row == self.max_row:
84 return 1 + self.max_col - self.min_col
85 return 1 + self.max_row - self.min_row
86
87
88 def __eq__(self, other):
89 return str(self) == str(other)
90
91
92 @property
93 def rows(self):
94 """
95 Return all rows in the range
96 """
97 for row in range(self.min_row, self.max_row+1):
98 yield Reference(self.worksheet, self.min_col, row, self.max_col, row)
99
100
101 @property
102 def cols(self):
103 """
104 Return all columns in the range
105 """
106 for col in range(self.min_col, self.max_col+1):
107 yield Reference(self.worksheet, col, self.min_row, col, self.max_row)
108
109
110 def pop(self):
111 """
112 Return and remove the first cell
113 """
114 cell = "{0}{1}".format(get_column_letter(self.min_col), self.min_row)
115 if self.min_row == self.max_row:
116 self.min_col += 1
117 else:
118 self.min_row += 1
119 return cell
120
121
122 @property
123 def sheetname(self):
124 return quote_sheetname(self.worksheet.title)