pyfastexcel 0.0.6__tar.gz → 0.0.7__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.
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyfastexcel
3
+ Version: 0.0.7
4
+ Summary: High performace excel file generation library.
5
+ Home-page: https://github.com/Zncl2222/pyfastexcel
6
+ Author: Zncl2222
7
+ Author-email: 3002shinning@gmail.com
8
+ License: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Go
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.7
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Requires-Python: >=3.7
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: msgspec
23
+ Requires-Dist: openpyxl-style-writer>=1.1.3
24
+
25
+ # pyfastexcel
26
+
27
+ ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/Zncl2222/pyfastexcel/go.yml?logo=go)
28
+ [![Go Report Card](https://goreportcard.com/badge/github.com/Zncl2222/pyfastexcel)](https://goreportcard.com/report/github.com/Zncl2222/pyfastexcel)
29
+ ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/Zncl2222/pyfastexcel/pre-commit.yml?logo=pre-commit&label=pre-commit)
30
+ ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/Zncl2222/pyfastexcel/codeql.yml?logo=github&label=CodeQL)
31
+ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/03f42030775045b791586dee20288905)](https://app.codacy.com/gh/Zncl2222/pyfastexcel/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
32
+ [![codecov](https://codecov.io/gh/Zncl2222/pyfastexcel/graph/badge.svg?token=6I03AWUUWL)](https://codecov.io/gh/Zncl2222/pyfastexcel)
33
+ [![Documentation Status](https://readthedocs.org/projects/pyfastexcel/badge/?version=stable)](https://pyfastexcel.readthedocs.io/en/stable/?badge=stable)
34
+
35
+ This package enables high-performance Excel writing by integrating with the
36
+ streaming API from the golang package
37
+ [excelize](https://github.com/qax-os/excelize). Users can leverage this
38
+ functionality without the need to write any Go code, as the entire process
39
+ can be accomplished through Python.
40
+
41
+ ## Features
42
+
43
+ - Python and Golang Integration: Seamlessly call Golang built shared
44
+ libraries from Python.
45
+
46
+ - No Golang Code Required: Users can solely rely on Python for Excel file
47
+ generation, eliminating the need for Golang expertise.
48
+
49
+ ## Installation
50
+
51
+ ### Install via pip (Recommended)
52
+
53
+ You can easily install the package via pip
54
+
55
+ ```bash
56
+ pip install pyfastexcel
57
+ ```
58
+
59
+ ### Install manually
60
+
61
+ If you prefer to build the package manually, follow these steps:
62
+
63
+ 1. Clone the repository:
64
+
65
+ ```bash
66
+ git clone https://github.com/Zncl2222/pyfastexcel.git
67
+ ```
68
+
69
+ 2. Go to the project root directory:
70
+
71
+ ```bash
72
+ cd pyfastexcel
73
+ ```
74
+
75
+ 3. Install the required golang packages:
76
+
77
+ ```bash
78
+ go mod download
79
+ ```
80
+
81
+ 4. Build the Golang shared library using the Makefile:
82
+
83
+ ```bash
84
+ make
85
+ ```
86
+
87
+ 5. Install the required python packages:
88
+
89
+ ```bash
90
+ pip install -r requirements.txt
91
+ ```
92
+
93
+ or
94
+
95
+ ```bash
96
+ pipenv install
97
+ ```
98
+
99
+ 6. Import the project and start using it!
100
+
101
+ ## Usage
102
+
103
+ The index assignment is now avaliable in `Workbook` and the `FastWriter`.
104
+ Here is the example usage:
105
+
106
+ ```python
107
+ from pyfastexcel import Workbook
108
+ from pyfastexcel.utils import set_custom_style
109
+
110
+ # CustomStyle will be integrate to the pyfatexcel in next version
111
+ # Beside, CustomStyle will be re-implement in future to make it no-longer
112
+ # depend on openpyxl_style writer and openpyxl
113
+ from openpyxl_style_writer import CustomStyle
114
+
115
+
116
+ if __name__ == '__main__':
117
+ # Workbook
118
+ wb = Workbook()
119
+
120
+ # Set and register CustomStyle
121
+ bold_style = CustomStyle(font_size=15, font_bold=True)
122
+ set_custom_style('bold_style', bold_style)
123
+
124
+ ws = wb['Sheet1']
125
+ # Write value with default style
126
+ ws['A1'] = 'A1 value'
127
+ # Write value with custom style
128
+ ws['B1'] = ('B1 value', 'bold_style')
129
+
130
+ # Write value in slice with default style
131
+ ws['A2': 'C2'] = [1, 2, 3]
132
+ # Write value in slice with custom style
133
+ ws['A3': 'C3'] = [(1, 'bold_style'), (2, 'bold_style'), (3, 'bold_style')]
134
+
135
+ # Write value by row with default style (python index 0 is the index 1 in excel)
136
+ ws[3] = [9, 8, 'go']
137
+ # Write value by row with custom style
138
+ ws[4] = [(9, 'bold_style'), (8, 'bold_style'), ('go', 'bold_style')]
139
+
140
+ # Send request to golang lib and create excel
141
+ wb.read_lib_and_create_excel()
142
+
143
+ # File path to save
144
+ file_path = 'pyexample_workbook.xlsx'
145
+ wb.save(file_path)
146
+
147
+ ```
148
+
149
+ You can also using the `FastWriter` or `NormalWriter` which was the
150
+ subclass of `Workbook` to write excel row by row, see the more in documentations [quickstart](https://pyfastexcel.readthedocs.io/en/stable/quickstart/).
151
+
152
+ ## Documentation
153
+
154
+ The documentation is hosted on Read the Docs.
155
+
156
+ - [Development Version](https://pyfastexcel.readthedocs.io/en/latest/)
157
+
158
+ - [Latest Stable Version](https://pyfastexcel.readthedocs.io/en/stable/)
159
+
160
+ ## How it Works
161
+
162
+ The core functionality revolves around encoding Excel cell data and styles,
163
+ or any other Excel properties, into a JSON string within Python. This JSON
164
+ payload is then passed through ctypes to a Golang shared library. In Golang,
165
+ the JSON is parsed, and using the streaming writer of
166
+ [excelize](https://github.com/qax-os/excelize) to wrtie excel in
167
+ high performance.
168
+
169
+ ## Current Limitations & Future Plans
170
+
171
+ ### Problem 1: Dependence on Other Excel Package
172
+
173
+ Limitations:
174
+
175
+ This project currently depends on the `CustomStyle` object of
176
+ the [openpyxl_style_writer](https://github.com/Zncl2222/openpyxl_style_writer)
177
+ package, which is built for openpyxl to write styles in write-only
178
+ mode more efficiently without duplicating code.
179
+
180
+ Future Plans:
181
+
182
+ This project plans to create its own `Style` object, making it no longer
183
+ dependent on the mentioned package.
@@ -0,0 +1,159 @@
1
+ # pyfastexcel
2
+
3
+ ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/Zncl2222/pyfastexcel/go.yml?logo=go)
4
+ [![Go Report Card](https://goreportcard.com/badge/github.com/Zncl2222/pyfastexcel)](https://goreportcard.com/report/github.com/Zncl2222/pyfastexcel)
5
+ ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/Zncl2222/pyfastexcel/pre-commit.yml?logo=pre-commit&label=pre-commit)
6
+ ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/Zncl2222/pyfastexcel/codeql.yml?logo=github&label=CodeQL)
7
+ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/03f42030775045b791586dee20288905)](https://app.codacy.com/gh/Zncl2222/pyfastexcel/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
8
+ [![codecov](https://codecov.io/gh/Zncl2222/pyfastexcel/graph/badge.svg?token=6I03AWUUWL)](https://codecov.io/gh/Zncl2222/pyfastexcel)
9
+ [![Documentation Status](https://readthedocs.org/projects/pyfastexcel/badge/?version=stable)](https://pyfastexcel.readthedocs.io/en/stable/?badge=stable)
10
+
11
+ This package enables high-performance Excel writing by integrating with the
12
+ streaming API from the golang package
13
+ [excelize](https://github.com/qax-os/excelize). Users can leverage this
14
+ functionality without the need to write any Go code, as the entire process
15
+ can be accomplished through Python.
16
+
17
+ ## Features
18
+
19
+ - Python and Golang Integration: Seamlessly call Golang built shared
20
+ libraries from Python.
21
+
22
+ - No Golang Code Required: Users can solely rely on Python for Excel file
23
+ generation, eliminating the need for Golang expertise.
24
+
25
+ ## Installation
26
+
27
+ ### Install via pip (Recommended)
28
+
29
+ You can easily install the package via pip
30
+
31
+ ```bash
32
+ pip install pyfastexcel
33
+ ```
34
+
35
+ ### Install manually
36
+
37
+ If you prefer to build the package manually, follow these steps:
38
+
39
+ 1. Clone the repository:
40
+
41
+ ```bash
42
+ git clone https://github.com/Zncl2222/pyfastexcel.git
43
+ ```
44
+
45
+ 2. Go to the project root directory:
46
+
47
+ ```bash
48
+ cd pyfastexcel
49
+ ```
50
+
51
+ 3. Install the required golang packages:
52
+
53
+ ```bash
54
+ go mod download
55
+ ```
56
+
57
+ 4. Build the Golang shared library using the Makefile:
58
+
59
+ ```bash
60
+ make
61
+ ```
62
+
63
+ 5. Install the required python packages:
64
+
65
+ ```bash
66
+ pip install -r requirements.txt
67
+ ```
68
+
69
+ or
70
+
71
+ ```bash
72
+ pipenv install
73
+ ```
74
+
75
+ 6. Import the project and start using it!
76
+
77
+ ## Usage
78
+
79
+ The index assignment is now avaliable in `Workbook` and the `FastWriter`.
80
+ Here is the example usage:
81
+
82
+ ```python
83
+ from pyfastexcel import Workbook
84
+ from pyfastexcel.utils import set_custom_style
85
+
86
+ # CustomStyle will be integrate to the pyfatexcel in next version
87
+ # Beside, CustomStyle will be re-implement in future to make it no-longer
88
+ # depend on openpyxl_style writer and openpyxl
89
+ from openpyxl_style_writer import CustomStyle
90
+
91
+
92
+ if __name__ == '__main__':
93
+ # Workbook
94
+ wb = Workbook()
95
+
96
+ # Set and register CustomStyle
97
+ bold_style = CustomStyle(font_size=15, font_bold=True)
98
+ set_custom_style('bold_style', bold_style)
99
+
100
+ ws = wb['Sheet1']
101
+ # Write value with default style
102
+ ws['A1'] = 'A1 value'
103
+ # Write value with custom style
104
+ ws['B1'] = ('B1 value', 'bold_style')
105
+
106
+ # Write value in slice with default style
107
+ ws['A2': 'C2'] = [1, 2, 3]
108
+ # Write value in slice with custom style
109
+ ws['A3': 'C3'] = [(1, 'bold_style'), (2, 'bold_style'), (3, 'bold_style')]
110
+
111
+ # Write value by row with default style (python index 0 is the index 1 in excel)
112
+ ws[3] = [9, 8, 'go']
113
+ # Write value by row with custom style
114
+ ws[4] = [(9, 'bold_style'), (8, 'bold_style'), ('go', 'bold_style')]
115
+
116
+ # Send request to golang lib and create excel
117
+ wb.read_lib_and_create_excel()
118
+
119
+ # File path to save
120
+ file_path = 'pyexample_workbook.xlsx'
121
+ wb.save(file_path)
122
+
123
+ ```
124
+
125
+ You can also using the `FastWriter` or `NormalWriter` which was the
126
+ subclass of `Workbook` to write excel row by row, see the more in documentations [quickstart](https://pyfastexcel.readthedocs.io/en/stable/quickstart/).
127
+
128
+ ## Documentation
129
+
130
+ The documentation is hosted on Read the Docs.
131
+
132
+ - [Development Version](https://pyfastexcel.readthedocs.io/en/latest/)
133
+
134
+ - [Latest Stable Version](https://pyfastexcel.readthedocs.io/en/stable/)
135
+
136
+ ## How it Works
137
+
138
+ The core functionality revolves around encoding Excel cell data and styles,
139
+ or any other Excel properties, into a JSON string within Python. This JSON
140
+ payload is then passed through ctypes to a Golang shared library. In Golang,
141
+ the JSON is parsed, and using the streaming writer of
142
+ [excelize](https://github.com/qax-os/excelize) to wrtie excel in
143
+ high performance.
144
+
145
+ ## Current Limitations & Future Plans
146
+
147
+ ### Problem 1: Dependence on Other Excel Package
148
+
149
+ Limitations:
150
+
151
+ This project currently depends on the `CustomStyle` object of
152
+ the [openpyxl_style_writer](https://github.com/Zncl2222/openpyxl_style_writer)
153
+ package, which is built for openpyxl to write styles in write-only
154
+ mode more efficiently without duplicating code.
155
+
156
+ Future Plans:
157
+
158
+ This project plans to create its own `Style` object, making it no longer
159
+ dependent on the mentioned package.
@@ -0,0 +1,13 @@
1
+ from openpyxl_style_writer import CustomStyle, DefaultStyle
2
+
3
+ from pyfastexcel.writer import FastWriter, NormalWriter, Workbook
4
+
5
+ __all__ = [
6
+ 'Workbook',
7
+ 'FastWriter',
8
+ 'NormalWriter',
9
+ # Temporary link the CustomStyle from openpyxl_style_writer for
10
+ # convinent usage.
11
+ 'CustomStyle',
12
+ 'DefaultStyle',
13
+ ]
@@ -11,12 +11,13 @@ from typing import Any
11
11
  import msgspec
12
12
  from openpyxl_style_writer import CustomStyle
13
13
 
14
- from .exceptions import CreateFileNotCalledError
15
14
  from .utils import (
16
15
  column_to_index,
17
16
  excel_index_to_list_index,
18
17
  extract_numeric_part,
19
18
  separate_alpha_numeric,
19
+ validate_and_format_value,
20
+ validate_and_register_style,
20
21
  )
21
22
 
22
23
  BASE_DIR = Path(__file__).resolve().parent
@@ -109,6 +110,7 @@ class ExcelDriver:
109
110
  self.file_props = self._get_default_file_props()
110
111
  self.sheet = 'Sheet1'
111
112
  self._sheet_list = tuple(['Sheet1'])
113
+ self._dict_wb = {}
112
114
 
113
115
  @property
114
116
  def sheet_list(self):
@@ -119,11 +121,16 @@ class ExcelDriver:
119
121
  cls.REGISTERED_STYLES[name] = custom_style
120
122
  cls._STYLE_NAME_MAP[custom_style] = name
121
123
 
124
+ @classmethod
125
+ def reset_style_configs(cls):
126
+ cls.REGISTERED_STYLES = {'DEFAULT_STYLE': cls.DEFAULT_STYLE}
127
+ cls._STYLE_NAME_MAP = {}
128
+ cls._STYLE_ID = 0
129
+ cls._style_map = {}
130
+
122
131
  def save(self, path: str = './pyfastexcel.xlsx') -> None:
123
132
  if not hasattr(self, 'decoded_bytes'):
124
- raise CreateFileNotCalledError(
125
- 'Function read_lib_and_create_excel should be ' + 'called before saving the file.',
126
- )
133
+ self.read_lib_and_create_excel()
127
134
 
128
135
  with open(path, 'wb') as file:
129
136
  file.write(self.decoded_bytes)
@@ -150,10 +157,10 @@ class ExcelDriver:
150
157
 
151
158
  # Transfer all WorkSheet Object to the sheet dictionary in the workbook.
152
159
  for sheet in self._sheet_list:
153
- self.workbook[sheet] = self.workbook[sheet]._transfer_to_dict()
160
+ self._dict_wb[sheet] = self.workbook[sheet]._transfer_to_dict()
154
161
 
155
162
  results = {
156
- 'content': self.workbook,
163
+ 'content': self._dict_wb,
157
164
  'file_props': self.file_props,
158
165
  'style': self._style_map,
159
166
  }
@@ -166,6 +173,7 @@ class ExcelDriver:
166
173
  byte_data = create_excel(json_data)
167
174
  self.decoded_bytes = base64.b64decode(ctypes.cast(byte_data, ctypes.c_char_p).value)
168
175
  free_pointer(byte_data)
176
+ ExcelDriver.reset_style_configs()
169
177
  return self.decoded_bytes
170
178
 
171
179
  def _read_lib(self, lib_path: str) -> ctypes.CDLL:
@@ -371,6 +379,9 @@ class WorkSheet:
371
379
  Validates the input value and ensures it is a tuple with the correct
372
380
  format.
373
381
 
382
+ set_style(target: str | slice | list[int, int], style: CustomStyle | str) -> None:
383
+ Applies a style to specified cells.
384
+
374
385
  __getitem__(key: str | slice | int) -> tuple | list[tuple]:
375
386
  If index_supported is True, retrieves the cell value at the
376
387
  specified index. Raises TypeError if index_supported is False.
@@ -427,6 +438,71 @@ class WorkSheet:
427
438
  self._expand_row_and_cols(row, column)
428
439
  self.data[row][column] = value
429
440
 
441
+ def set_style(
442
+ self,
443
+ target: str | slice | list[int, int],
444
+ style: CustomStyle | str,
445
+ ) -> None:
446
+ """
447
+ Applies a specified style to a target range of cells.
448
+
449
+ Args:
450
+ target (str | slice | list[int, int]): Target cells to apply style.
451
+ style (CustomStyle | str): Style to apply to the cells.
452
+
453
+ Raises:
454
+ TypeError: If target type is invalid.
455
+ ValueError: If style is not registered.
456
+ """
457
+ if self.index_supported is False:
458
+ raise IndexError('Index is not supported in this Writer.')
459
+
460
+ if isinstance(style, str):
461
+ if ExcelDriver.REGISTERED_STYLES.get(style) is None:
462
+ raise ValueError(
463
+ f'Style not found: {style}. Style should be register by '
464
+ 'set_custom_style function when you set a style with '
465
+ 'string.',
466
+ )
467
+ elif isinstance(style, CustomStyle):
468
+ if ExcelDriver._STYLE_NAME_MAP.get(style) is None:
469
+ validate_and_register_style(style)
470
+ style = ExcelDriver._STYLE_NAME_MAP[style]
471
+
472
+ if isinstance(target, str):
473
+ self._apply_style_to_string_target(target, style)
474
+ elif isinstance(target, slice):
475
+ self._apply_style_to_slice_target(target, style)
476
+ elif isinstance(target, list) and len(target) == 2:
477
+ self._apply_style_to_list_target(target, style)
478
+ else:
479
+ raise TypeError('Target should be a string, slice, or list[row, index].')
480
+
481
+ def _apply_style_to_string_target(self, target: str, style: str):
482
+ if ':' not in target:
483
+ row, col = excel_index_to_list_index(target)
484
+ self.data[row][col] = (self.data[row][col][0], style)
485
+ else:
486
+ target_slice = target.split(':')
487
+ target = slice(target_slice[0], target_slice[1])
488
+ self._apply_style_to_slice_target(target, style)
489
+
490
+ def _apply_style_to_slice_target(self, target: slice, style: str):
491
+ start_row, start_col, col_stop = self._extract_slice_indices(target)
492
+ for col in range(start_col, col_stop + 1):
493
+ self.data[start_row][col] = (self.data[start_row][col][0], style)
494
+
495
+ def _apply_style_to_list_target(self, target: list[int, int], style: str):
496
+ row = target[0]
497
+ col = target[1]
498
+ if not isinstance(row, int) or not isinstance(col, int):
499
+ raise TypeError('Target should be a list of integers.')
500
+ if row < 0 or row > 1048576:
501
+ raise ValueError(f'Invalid row index: {row}')
502
+ if col < 0 or col > 16384:
503
+ raise ValueError(f'Invalid column index: {col}')
504
+ self.data[row][col] = (self.data[row][col][0], style)
505
+
430
506
  def set_cell_width(self, col: str | int, value: int) -> None:
431
507
  if isinstance(col, str):
432
508
  col = column_to_index(col)
@@ -550,19 +626,25 @@ class WorkSheet:
550
626
  or a CustomStyle object.
551
627
  """
552
628
  if not isinstance(value, tuple):
553
- # Covert to string if value is not numeric or string
554
- if not isinstance(value, (int, float, str)):
555
- value = f'{value}'
556
- value = (value, 'DEFAULT_STYLE')
629
+ value = validate_and_format_value(value)
557
630
  else:
558
631
  if len(value) != 2:
559
632
  raise ValueError(
560
- 'Cell value should be a tuple with two element' ' like (value, style).',
633
+ 'Cell value should be a tuple with two element like (value, style).',
561
634
  )
562
635
  if not isinstance(value[1], (str, CustomStyle)):
563
636
  raise TypeError(
564
637
  'Style should be a string or CustomStyle object.',
565
638
  )
639
+ # The case that user do not register the Custom Style by 'Class attributes'
640
+ # or set_cumston_style function.
641
+ if (
642
+ isinstance(value[1], CustomStyle)
643
+ and ExcelDriver._STYLE_NAME_MAP.get(value[1]) is None
644
+ ):
645
+ validate_and_register_style(value[1])
646
+ style = ExcelDriver._STYLE_NAME_MAP[value[1]]
647
+ value = (value[0], style)
566
648
  return value
567
649
 
568
650
  def __getitem__(self, key: str | slice) -> tuple | list[tuple]:
@@ -600,7 +682,7 @@ class WorkSheet:
600
682
  row, col = excel_index_to_list_index(key)
601
683
  return self.data[row][col]
602
684
 
603
- def _set_cell_by_slice(self, cell_slice: slice, value: Any) -> None:
685
+ def _extract_slice_indices(self, cell_slice: slice) -> tuple[int, int, int]:
604
686
  start_row = extract_numeric_part(cell_slice.start)
605
687
  stop_row = extract_numeric_part(cell_slice.stop)
606
688
  if start_row != stop_row:
@@ -608,6 +690,10 @@ class WorkSheet:
608
690
  start_row, start_col = excel_index_to_list_index(cell_slice.start)
609
691
  _, col_stop = excel_index_to_list_index(cell_slice.stop)
610
692
  self._expand_row_and_cols(start_row, col_stop)
693
+ return start_row, start_col, col_stop
694
+
695
+ def _set_cell_by_slice(self, cell_slice: slice, value: Any) -> None:
696
+ start_row, start_col, col_stop = self._extract_slice_indices(cell_slice)
611
697
  for idx, col in enumerate(range(start_col, col_stop + 1)):
612
698
  val = self._validate_value_and_set_default(value[idx])
613
699
  self.data[start_row][col] = val
@@ -2,17 +2,18 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
  import string
5
+ from typing import Any, Literal
5
6
 
6
7
  from openpyxl_style_writer import CustomStyle
7
8
 
8
9
 
9
- def set_custom_style(style_name: str, style: CustomStyle):
10
+ def set_custom_style(style_name: str, style: CustomStyle) -> None:
10
11
  from .driver import ExcelDriver
11
12
 
12
13
  ExcelDriver.set_custom_style(style_name, style)
13
14
 
14
15
 
15
- def style_validation(style: CustomStyle):
16
+ def validate_and_register_style(style: CustomStyle) -> None:
16
17
  from .driver import ExcelDriver
17
18
 
18
19
  if not isinstance(style, CustomStyle):
@@ -23,6 +24,19 @@ def style_validation(style: CustomStyle):
23
24
  ExcelDriver._STYLE_ID += 1
24
25
 
25
26
 
27
+ def validate_and_format_value(
28
+ value: Any,
29
+ set_default_style: bool = True,
30
+ ) -> tuple[Any, Literal['DEFAULT_STYLE']] | Any:
31
+ # Convert non-numeric value to string
32
+ value = f'{value}' if not isinstance(value, (int, float, str)) else value
33
+ # msgpec does not support np.float64, so we should convert
34
+ # it to python float.
35
+ value = float(value) if isinstance(value, float) else value
36
+
37
+ return (value, 'DEFAULT_STYLE') if set_default_style else value
38
+
39
+
26
40
  def separate_alpha_numeric(input_string: str):
27
41
  alpha_part = re.findall(r'[a-zA-Z]+', input_string)
28
42
  num_part = re.findall(r'[0-9]+', input_string)
@@ -1,10 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import Any
4
+
3
5
  from openpyxl_style_writer import CustomStyle
4
6
 
5
7
  from pyfastexcel.driver import ExcelDriver, WorkSheet
6
8
 
7
- from .utils import style_validation
9
+ from .utils import validate_and_format_value, validate_and_register_style
8
10
 
9
11
 
10
12
  class Workbook(ExcelDriver):
@@ -149,18 +151,19 @@ class FastWriter(Workbook):
149
151
  self.current_row = 0
150
152
  self.current_col = 0
151
153
 
152
- def row_append(self, value: str, style: str | CustomStyle = 'DEFAULT_STYLE'):
154
+ def row_append(self, value: Any, style: str | CustomStyle = 'DEFAULT_STYLE'):
153
155
  """
154
156
  Appends a value to a specific row and column.
155
157
 
156
158
  Args:
157
- value (str): The value to be appended.
159
+ value (Any): The value to be appended.
158
160
  style (str): The style of the value.
159
161
  """
160
162
  if isinstance(style, CustomStyle):
161
163
  if self._STYLE_NAME_MAP.get(style) is None:
162
- style_validation(style)
164
+ validate_and_register_style(style)
163
165
  style = self._STYLE_NAME_MAP[style]
166
+ value = validate_and_format_value(value, set_default_style=False)
164
167
  self._row_list[self.current_row][self.current_col] = (value, style)
165
168
  self.current_col += 1
166
169
 
@@ -236,28 +239,25 @@ class NormalWriter(Workbook):
236
239
  self._row_list = []
237
240
  self.data = data
238
241
 
239
- def row_append(self, value: str, style: str | CustomStyle = 'DEFAULT_STYLE'):
242
+ def row_append(self, value: Any, style: str | CustomStyle = 'DEFAULT_STYLE'):
240
243
  """
241
244
  Appends a value to the row list.
242
245
 
243
246
  Args:
244
- value (str): The value to be appended.
247
+ value (Any): The value to be appended.
245
248
  style (str | CustomStyle): The style of the value, can be either
246
249
  a style name or a CustomStyle object.
247
250
  """
248
251
  if isinstance(style, CustomStyle):
249
252
  if self._STYLE_NAME_MAP.get(style) is None:
250
- style_validation(style)
253
+ validate_and_register_style(style)
251
254
  style = self._STYLE_NAME_MAP[style]
255
+ value = validate_and_format_value(value, set_default_style=False)
252
256
  self._row_list.append((value, style))
253
257
 
254
258
  def create_row(self):
255
259
  """
256
260
  Creates a row in the Excel data, and clean the current _row_list.
257
-
258
- Args:
259
- is_header (bool, optional): Indicates whether the row is a header
260
- row. Defaults to False.
261
261
  """
262
262
  self.workbook[self.sheet].data.append(self._row_list)
263
263
  self._row_list = []