Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ jobs:

- name: Upload Linux Artifacts
if: matrix.os == 'ubuntu-24.04'
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: linux-artifacts
path: |
Expand All @@ -161,15 +161,15 @@ jobs:

- name: Upload Linux ARM64 Artifacts
if: matrix.os == 'ubuntu-24.04-arm'
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: linux-arm64-artifacts
path: |
libexcelize.arm64.linux.so

- name: Upload Darwin Artifacts
if: matrix.os == 'macos-latest'
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: darwin-artifacts
path: |
Expand Down Expand Up @@ -199,7 +199,7 @@ jobs:
uses: actions/checkout@v6

- name: Download Artifacts
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
merge-multiple: true
path: ./
Expand Down
150 changes: 117 additions & 33 deletions excelize.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,38 +504,6 @@ def c_value_to_py_interface(c_value):
raise TypeError(f"unsupported interface type code: {c_value.Type}")


def prepare_args(args: List, types: List[argsRule]):
"""
Validate arguments against expected types.

Args:
args (List): The arguments to validate.
types (List[argsRule]): The expected argument rules.

Raises:
TypeError: If an argument type doesn't match the expected types.
"""
if not types:
return
opts = types[-1].opts if types else False
for i, excepted in enumerate(types):
if opts and i >= len(args):
return
if i >= len(args):
continue
received = type(args[i])
if received not in excepted.types:
names = [t.__name__ for t in excepted.types]
if len(names) == 1:
t = names[0]
else:
t = ", ".join(names[:-1]) + f" or {names[-1]}"
raise TypeError(
f"expected type {t} for argument "
f"'{excepted.name}', but got {received.__name__}"
)


class MergeCell:
"""
MergeCell define a merged cell data. It consists of the following structure.
Expand Down Expand Up @@ -602,7 +570,7 @@ def columns(self, *opts: Options) -> List[str]:
rows in the tail of the worksheet.

Args:
*opts (Options): Optional parameters for get column cells value.
*opts (Options): Optional parameters for get column cells value

Returns:
List[str]: Return the current row's column values if no error
Expand Down Expand Up @@ -1586,6 +1554,89 @@ def add_comment(self, sheet: str, opts: Comment) -> None:
if err != "":
raise RuntimeError(err)

def add_data_validation(self, sheet: str, dv: DataValidation) -> None:
"""
Set data validation on a range of the worksheet by given data validation
object and worksheet name. The data validation object can be created by
`new_data_validation` function.

Args:
sheet (str): The worksheet name
dv (DataValidation): The data validation options

Returns:
None: Return None if no error occurred, otherwise raise a
RuntimeError with the message.

Example:
Example 1, set data validation on Sheet1!A1:B2 with validation
criteria settings, show error alert after invalid data is entered
with "Stop" style and custom title "error body":

```python
try:
dv = excelize.new_data_validation(True)
dv.sqref = "A1:B2"
dv.set_range(
10,
20,
excelize.DataValidationType.DataValidationTypeWhole,
excelize.DataValidationOperator.DataValidationOperatorBetween,
)
dv.set_error(
excelize.DataValidationErrorStyle.DataValidationErrorStyleStop,
"error title",
"error body",
)
f.add_data_validation("Sheet1", dv)
except (RuntimeError, TypeError) as err:
print(err)
```

Example 2, set data validation on Sheet1!A3:B4 with validation
criteria settings, and show input message when cell is selected:

```python
try:
dv = excelize.new_data_validation(True)
dv.sqref = "A3:B4"
dv.set_range(
10,
20,
excelize.DataValidationType.DataValidationTypeWhole,
excelize.DataValidationOperator.DataValidationOperatorGreaterThan,
)
dv.set_input("input title", "input body")
f.add_data_validation("Sheet1", dv)
except (RuntimeError, TypeError) as err:
print(err)
```

Example 3, set data validation on Sheet1!A5:B6 with validation
criteria settings, create in-cell dropdown by allowing list source:

```python
try:
dv = excelize.new_data_validation(True)
dv.sqref = "A5:B6"
dv.set_drop_list(["1", "2", "3"])
f.add_data_validation("Sheet1", dv)
except (RuntimeError, TypeError) as err:
print(err)
```
"""
prepare_args(
[sheet, dv],
[argsRule("sheet", [str]), argsRule("dv", [DataValidation])],
)
lib.AddDataValidation.restype = c_char_p
options = py_value_to_c(dv, types_go._DataValidation())
err = lib.AddDataValidation(
self.file_index, sheet.encode(ENCODE), byref(options)
).decode(ENCODE)
if err != "":
raise RuntimeError(err)

def add_form_control(self, sheet: str, opts: FormControl) -> None:
"""
Add form control object in a worksheet by given worksheet name and form
Expand Down Expand Up @@ -3304,6 +3355,26 @@ def get_custom_props(self) -> List[CustomProperty]:
return arr
raise RuntimeError(err)

def get_data_validations(self, sheet: str) -> List[DataValidation]:
"""
Get data validations of a worksheet by given worksheet name.

Args:
sheet (str): The worksheet name

Returns:
List[DataValidation]: Return data validations if no error occurred,
otherwise raise a RuntimeError with the message.
"""
prepare_args([sheet], [argsRule("sheet", [str])])
lib.GetDataValidations.restype = types_go._GetDataValidationsResult
res = lib.GetDataValidations(self.file_index, sheet.encode(ENCODE))
dvs = c_value_to_py(res, GetDataValidationsResult()).dvs
err = res.Err.decode(ENCODE)
if not err:
return dvs if dvs else []
raise RuntimeError(err)

def get_default_font(self) -> str:
"""
Get the default font name currently set in the workbook. The spreadsheet
Expand Down Expand Up @@ -6877,6 +6948,19 @@ def open_reader(buffer: bytes, *opts: Options) -> Optional[File]:
raise RuntimeError(err)


def new_data_validation(allow_blank: bool) -> DataValidation:
"""
Create a new data validation object.

Args:
allow_blank (bool): Whether to allow blank values.

Returns:
DataValidation: A new data validation object.
"""
return DataValidation(allow_blank=allow_blank)


def split_cell_name(cell: str) -> Tuple[str, int]:
"""
Splits cell name to column name and row number.
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrI
github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.10.1 h1:V62UlqopMqha3kOpnlHy2CcRVw1V8E63jFoWUmMzxN0=
github.com/xuri/excelize/v2 v2.10.1/go.mod h1:iG5tARpgaEeIhTqt3/fgXCGoBRt4hNXgCp3tfXKoOIc=
github.com/xuri/excelize/v2 v2.10.2-0.20260227001322-3e9bc141d4a4 h1:R7nQI8A8ZgZp+8QMcisIYqqrK1mkPOqdE3BAsw0vjtM=
github.com/xuri/excelize/v2 v2.10.2-0.20260227001322-3e9bc141d4a4/go.mod h1:iG5tARpgaEeIhTqt3/fgXCGoBRt4hNXgCp3tfXKoOIc=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
Expand Down
46 changes: 46 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,29 @@ func AddComment(idx int, sheet *C.char, opts *C.struct_Comment) *C.char {
return C.CString(emptyString)
}

// AddDataValidation provides set data validation on a range of the worksheet
// by given data validation object and worksheet name. This function is
// concurrency safe. The data validation object can be created by
// NewDataValidation function.
//
//export AddDataValidation
func AddDataValidation(idx int, sheet *C.char, dv *C.struct_DataValidation) *C.char {
var dataValidation excelize.DataValidation
goVal, err := cValueToGo(reflect.ValueOf(*dv), reflect.TypeOf(excelize.DataValidation{}))
if err != nil {
return C.CString(err.Error())
}
dataValidation = goVal.Elem().Interface().(excelize.DataValidation)
f, ok := files.Load(idx)
if !ok {
return C.CString(errFilePtr)
}
if err := f.(*excelize.File).AddDataValidation(C.GoString(sheet), &dataValidation); err != nil {
return C.CString(err.Error())
}
return C.CString(emptyString)
}

// AddFormControl provides the method to add form control object in a worksheet
// by given worksheet name and form control options. Supported form control
// type: button, check box, group box, label, option button, scroll bar and
Expand Down Expand Up @@ -1386,6 +1409,29 @@ func GetCustomProps(idx int) C.struct_GetCustomPropsResult {
return C.struct_GetCustomPropsResult{CustomPropsLen: C.int(len(props)), CustomProps: (*C.struct_CustomProperty)(cArray), Err: C.CString(emptyString)}
}

// GetDataValidations returns data validations list by given worksheet name.
//
//export GetDataValidations
func GetDataValidations(idx int, sheet *C.char) C.struct_GetDataValidationsResult {
f, ok := files.Load(idx)
if !ok {
return C.struct_GetDataValidationsResult{Err: C.CString(errFilePtr)}
}
dvs, err := f.(*excelize.File).GetDataValidations(C.GoString(sheet))
if err != nil {
return C.struct_GetDataValidationsResult{Err: C.CString(err.Error())}
}
cArray := C.malloc(C.size_t(len(dvs)) * C.size_t(unsafe.Sizeof(C.struct_DataValidation{})))
for i, v := range dvs {
cVal, err := goValueToC(reflect.ValueOf(*v), reflect.ValueOf(&C.struct_DataValidation{}))
if err != nil {
return C.struct_GetDataValidationsResult{Err: C.CString(err.Error())}
}
*(*C.struct_DataValidation)(unsafe.Pointer(uintptr(unsafe.Pointer(cArray)) + uintptr(i)*unsafe.Sizeof(C.struct_DataValidation{}))) = cVal.Elem().Interface().(C.struct_DataValidation)
}
return C.struct_GetDataValidationsResult{DvsLen: C.int(len(dvs)), Dvs: (*C.struct_DataValidation)(cArray), Err: C.CString(emptyString)}
}

// GetDefaultFont provides the default font name currently set in the
// workbook. The spreadsheet generated by excelize default font is Calibri.
//
Expand Down
Loading