pyfastexcel 0.1.0__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/PKG-INFO +1 -1
  2. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel/__init__.py +2 -0
  3. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel/_typing.py +5 -1
  4. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel/driver.py +44 -9
  5. pyfastexcel-0.1.2/pyfastexcel/logformatter.py +38 -0
  6. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel/pyfastexcel.dll +0 -0
  7. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel/pyfastexcel.so +0 -0
  8. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel/style.py +14 -10
  9. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel/utils.py +5 -0
  10. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel/workbook.py +56 -6
  11. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel/worksheet.py +405 -289
  12. pyfastexcel-0.1.2/pyfastexcel/writer.py +150 -0
  13. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel.egg-info/PKG-INFO +1 -1
  14. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel.egg-info/SOURCES.txt +1 -0
  15. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/setup.cfg +2 -1
  16. pyfastexcel-0.1.0/pyfastexcel/writer.py +0 -60
  17. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/LICENSE +0 -0
  18. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/README.md +0 -0
  19. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel.egg-info/dependency_links.txt +0 -0
  20. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel.egg-info/not-zip-safe +0 -0
  21. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel.egg-info/requires.txt +0 -0
  22. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/pyfastexcel.egg-info/top_level.txt +0 -0
  23. {pyfastexcel-0.1.0 → pyfastexcel-0.1.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyfastexcel
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: High performace excel file generation library.
5
5
  Home-page: https://github.com/Zncl2222/pyfastexcel
6
6
  Author: Zncl2222
@@ -2,6 +2,7 @@ from openpyxl_style_writer import CustomStyle, DefaultStyle
2
2
 
3
3
  from pyfastexcel.workbook import Workbook
4
4
  from pyfastexcel.writer import StreamWriter
5
+ from pyfastexcel.utils import set_debug_level
5
6
 
6
7
  __all__ = [
7
8
  'Workbook',
@@ -10,4 +11,5 @@ __all__ = [
10
11
  # convinent usage.
11
12
  'CustomStyle',
12
13
  'DefaultStyle',
14
+ 'set_debug_level',
13
15
  ]
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Literal, TypedDict, Optional, List, Union
3
+ from typing import Literal, Protocol, TypedDict, Optional, List, Union
4
4
 
5
5
 
6
6
  class CommentTextDict(TypedDict, total=False):
@@ -21,5 +21,9 @@ class SelectionDict(TypedDict, total=False):
21
21
  pane: str
22
22
 
23
23
 
24
+ class Writable(Protocol):
25
+ def write(self, content: str) -> None: ...
26
+
27
+
24
28
  CommentTextStructure = Union[str, List[str], CommentTextDict, List[CommentTextDict]]
25
29
  SetPanesSelection = List[SelectionDict]
@@ -2,20 +2,31 @@ from __future__ import annotations
2
2
 
3
3
  import base64
4
4
  import ctypes
5
+ import logging
5
6
  import sys
6
7
  from datetime import datetime, timezone
7
8
  from io import BytesIO
8
9
  from pathlib import Path
10
+ from typing import overload
9
11
 
10
12
  import msgspec
11
13
  from openpyxl import load_workbook
12
14
  from openpyxl_style_writer import CustomStyle
13
15
 
16
+ from .logformatter import formatter
14
17
  from .style import StyleManager
15
18
  from .worksheet import WorkSheet
19
+ from ._typing import Writable
16
20
 
17
21
  BASE_DIR = Path(__file__).resolve().parent
18
22
 
23
+ logger = logging.getLogger(__name__)
24
+ style_formatter = logging.StreamHandler()
25
+ style_formatter.setFormatter(formatter)
26
+
27
+ logger.addHandler(style_formatter)
28
+ logger.propagate = False
29
+
19
30
 
20
31
  class ExcelDriver:
21
32
  """
@@ -85,14 +96,37 @@ class ExcelDriver:
85
96
 
86
97
  @property
87
98
  def sheet_list(self):
88
- return self._sheet_list
99
+ return list(self._sheet_list)
100
+
101
+ @overload
102
+ def save(self, file: Writable) -> None:
103
+ """
104
+ Saves the workbook to a writable object.
105
+
106
+ Args:
107
+ file (Writable): Writable object that has .write() function.
108
+ """
109
+ ...
89
110
 
90
- def save(self, path: str = './pyfastexcel.xlsx') -> None:
111
+ @overload
112
+ def save(self, path: str) -> None:
113
+ """
114
+ Saves the workbook to a file.
115
+
116
+ Args:
117
+ path (str): A path to save the file.
118
+ """
119
+ ...
120
+
121
+ def save(self, file_or_path: Writable | str) -> None:
91
122
  if not hasattr(self, 'decoded_bytes'):
92
123
  self.read_lib_and_create_excel()
93
124
 
94
- with open(path, 'wb') as file:
95
- file.write(self.decoded_bytes)
125
+ if isinstance(file_or_path, str):
126
+ with open(file_or_path, 'wb') as file:
127
+ file.write(self.decoded_bytes)
128
+ else:
129
+ file_or_path.write(self.decoded_bytes)
96
130
 
97
131
  def __getitem__(self, key: str) -> WorkSheet:
98
132
  return self.workbook[key]
@@ -120,11 +154,11 @@ class ExcelDriver:
120
154
  use_openpyxl = False
121
155
  for sheet in self._sheet_list:
122
156
  self._dict_wb[sheet] = self.workbook[sheet]._transfer_to_dict()
123
- if self.workbook[sheet].engine == 'openpyxl':
157
+ if self.workbook[sheet]._engine == 'openpyxl':
124
158
  use_openpyxl = True
125
- if len(self.workbook[sheet].grouped_columns) != 0:
159
+ if len(self.workbook[sheet]._grouped_columns_list) != 0:
126
160
  set_group_columns = True
127
- if len(self.workbook[sheet].grouped_rows) != 0:
161
+ if len(self.workbook[sheet]._grouped_rows_list) != 0:
128
162
  set_row_columns = True
129
163
 
130
164
  # Set writer (if some of the function that excelize StremWriter is not support, and
@@ -155,6 +189,7 @@ class ExcelDriver:
155
189
  # Due to Streaming API of Excelize can't group column currently
156
190
  # So implement this function by openpyxl
157
191
  if (set_group_columns or set_row_columns) and use_openpyxl is True:
192
+ logger.info('Using openpyxl to group columns and rows...')
158
193
  self.decoded_bytes = self._set_group_columns_and_group_rows()
159
194
 
160
195
  return self.decoded_bytes
@@ -162,7 +197,7 @@ class ExcelDriver:
162
197
  def _set_group_columns_and_group_rows(self):
163
198
  wb = load_workbook(BytesIO(self.decoded_bytes))
164
199
  for sheet in self._sheet_list:
165
- grouped_columns = self.workbook[sheet].grouped_columns
200
+ grouped_columns = self.workbook[sheet]._grouped_columns_list
166
201
  ws = wb[sheet]
167
202
  for col in grouped_columns:
168
203
  ws.column_dimensions.group(
@@ -171,7 +206,7 @@ class ExcelDriver:
171
206
  outline_level=col['outline_level'],
172
207
  hidden=col['hidden'],
173
208
  )
174
- grouped_rows = self.workbook[sheet].grouped_rows
209
+ grouped_rows = self.workbook[sheet]._grouped_rows_list
175
210
  for row in grouped_rows:
176
211
  ws.row_dimensions.group(
177
212
  row['start_row'],
@@ -0,0 +1,38 @@
1
+ import logging
2
+ import warnings
3
+
4
+ NORMAL_FORMAT = '\033[%(log_color)s%(asctime)s - %(name)s - %(levelname)s - %(message)s\033[0m'
5
+ LOG_COLORS = {
6
+ 'DEBUG': '\033[90m', # Grey
7
+ 'INFO': '\033[92m', # Green
8
+ 'WARNING': '\033[93m', # Yellow
9
+ 'ERROR': '\033[91m', # Red
10
+ 'CRITICAL': '\033[91m', # Red
11
+ }
12
+ LOG_CACHE = {}
13
+
14
+
15
+ def custom_warning_format(message, *args):
16
+ start_color = '\033[93m'
17
+ reset_color = '\033[0m'
18
+ return f'{start_color}DeprecationWarning: {message}{reset_color}\n'
19
+
20
+
21
+ def log_warning(logger: logging.Logger, message: str):
22
+ if not LOG_CACHE.get(message):
23
+ logger.warning(message)
24
+ LOG_CACHE[message] = True
25
+
26
+
27
+ class ColoredFormatter(logging.Formatter):
28
+ def __init__(self, fmt):
29
+ super().__init__(fmt)
30
+
31
+ def format(self, record):
32
+ record.log_color = LOG_COLORS[record.levelname]
33
+ return super().format(record)
34
+
35
+
36
+ warnings.formatwarning = custom_warning_format
37
+ warnings.simplefilter('always', DeprecationWarning)
38
+ formatter = ColoredFormatter(NORMAL_FORMAT)
@@ -5,20 +5,16 @@ from pathlib import Path
5
5
  from typing import Any
6
6
 
7
7
  from openpyxl_style_writer import CustomStyle
8
+ from .logformatter import formatter, log_warning
8
9
 
9
10
  BASE_DIR = Path(__file__).resolve().parent
10
11
 
11
12
  logger = logging.getLogger(__name__)
12
- logger.setLevel(logging.DEBUG)
13
+ style_formatter = logging.StreamHandler()
14
+ style_formatter.setFormatter(formatter)
13
15
 
14
- ch = logging.StreamHandler()
15
- ch.setLevel(logging.DEBUG)
16
-
17
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
18
- ch.setFormatter(formatter)
19
-
20
- if not logger.hasHandlers():
21
- logger.addHandler(ch)
16
+ logger.addHandler(style_formatter)
17
+ logger.propagate = False
22
18
 
23
19
  # TODO: Implement a CustomStyle without the dependency of openpyxl_style_writer
24
20
 
@@ -75,6 +71,11 @@ class StyleManager:
75
71
 
76
72
  @classmethod
77
73
  def set_custom_style(cls, name: str, custom_style: CustomStyle):
74
+ if cls.REGISTERED_STYLES.get(name):
75
+ log_warning(
76
+ logger,
77
+ f'{name} has already existed. Overiding the style settings.',
78
+ )
78
79
  cls.REGISTERED_STYLES[name] = custom_style
79
80
  cls._STYLE_NAME_MAP[custom_style] = name
80
81
 
@@ -104,7 +105,10 @@ class StyleManager:
104
105
 
105
106
  def _update_style_map(self, style_name: str, custom_style: CustomStyle) -> None:
106
107
  if self._style_map.get(style_name):
107
- logger.warning(f'{style_name} has already existed. Overiding the style settings.')
108
+ log_warning(
109
+ logger,
110
+ f'{style_name} has already existed. Overiding the style settings.',
111
+ )
108
112
  self._style_map[style_name] = self._get_default_style()
109
113
  self._style_map[style_name]['Font'] = self._get_font_style(custom_style)
110
114
  self._style_map[style_name]['Fill'] = self._get_fill_style(custom_style)
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
3
4
  import re
4
5
  import string
5
6
  import warnings
@@ -49,6 +50,10 @@ class Selection:
49
50
  }
50
51
 
51
52
 
53
+ def set_debug_level(level: int):
54
+ logging.basicConfig(level=level)
55
+
56
+
52
57
  def deprecated_warning(msg: str):
53
58
  warnings.warn(
54
59
  msg,
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Literal, Optional
3
+ from typing import Literal, Optional, overload
4
4
 
5
5
  from pyfastexcel.driver import ExcelDriver, WorkSheet
6
6
  from pyfastexcel.utils import deprecated_warning
@@ -16,6 +16,8 @@ class Workbook(ExcelDriver):
16
16
  Methods:
17
17
  remove_sheet(sheet: str) -> None:
18
18
  Removes a sheet from the Excel data.
19
+ rename_sheet(self, old_sheet_name: str, new_sheet_name: str) -> None:
20
+ Rename a sheet.
19
21
  create_sheet(sheet_name: str) -> None:
20
22
  Creates a new sheet.
21
23
  switch_sheet(sheet_name: str) -> None:
@@ -45,6 +47,24 @@ class Workbook(ExcelDriver):
45
47
  self._sheet_list = tuple(self.workbook.keys())
46
48
  self.sheet = self._sheet_list[0]
47
49
 
50
+ def rename_sheet(self, old_sheet_name: str, new_sheet_name: str) -> None:
51
+ """
52
+ Renames a sheet in the Excel data.
53
+
54
+ Args:
55
+ old_sheet_name (str): The name of the sheet to rename.
56
+ new_sheet_name (str): The new name for the sheet.
57
+ """
58
+ if self.workbook.get(old_sheet_name) is None:
59
+ raise IndexError(f'Sheet {old_sheet_name} does not exist.')
60
+ if self.workbook.get(new_sheet_name) is not None:
61
+ raise ValueError(f'Sheet {new_sheet_name} already exists.')
62
+ self.workbook[new_sheet_name] = self.workbook.pop(old_sheet_name)
63
+ self._sheet_list = tuple(
64
+ [new_sheet_name if x == old_sheet_name else x for x in self._sheet_list]
65
+ )
66
+ self.sheet = new_sheet_name
67
+
48
68
  def create_sheet(
49
69
  self,
50
70
  sheet_name: str,
@@ -121,15 +141,45 @@ class Workbook(ExcelDriver):
121
141
  self._check_if_sheet_exists(sheet)
122
142
  self.workbook[sheet].set_cell_height(row, value)
123
143
 
124
- def set_merge_cell(self, sheet: str, top_left_cell: str, bottom_right_cell: str) -> None:
144
+ @overload
145
+ def set_merge_cell(
146
+ self,
147
+ sheet: str,
148
+ top_lef_cell: Optional[str],
149
+ bottom_right_cell: Optional[str],
150
+ ) -> None: ...
151
+
152
+ @overload
153
+ def set_merge_cell(
154
+ self,
155
+ sheet: str,
156
+ cell_range: Optional[str],
157
+ ) -> None: ...
158
+
159
+ def set_merge_cell(self, sheet, *args) -> None:
125
160
  deprecated_warning(
126
- "This function is going to deprecated in v1.0.0. Please use 'wb.merge_cell' instead",
161
+ "wb.set_merge_cell is going to deprecated in v1.0.0. Please use 'wb.merge_cell' instead",
127
162
  )
128
- self.merge_cell(sheet, top_left_cell, bottom_right_cell)
163
+ self.merge_cell(sheet, *args)
164
+
165
+ @overload
166
+ def merge_cell(
167
+ self,
168
+ sheet: str,
169
+ top_lef_cell: Optional[str],
170
+ bottom_right_cell: Optional[str],
171
+ ) -> None: ...
172
+
173
+ @overload
174
+ def merge_cell(
175
+ self,
176
+ sheet: str,
177
+ cell_range: Optional[str],
178
+ ) -> None: ...
129
179
 
130
- def merge_cell(self, sheet: str, top_left_cell: str, bottom_right_cell: str) -> None:
180
+ def merge_cell(self, sheet: str, *args) -> None:
131
181
  self._check_if_sheet_exists(sheet)
132
- self.workbook[sheet].set_merge_cell(top_left_cell, bottom_right_cell)
182
+ self.workbook[sheet].set_merge_cell(*args)
133
183
 
134
184
  def auto_filter(self, sheet: str, target_range: str) -> None:
135
185
  self._check_if_sheet_exists(sheet)