Skip to content

Commit 5597198

Browse files
committed
Add 2 new functions: get_hyperlink_cells and get_sheet_protection
- Update unit tests
1 parent f0d2ad6 commit 5597198

5 files changed

Lines changed: 156 additions & 3 deletions

File tree

excelize.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3356,6 +3356,39 @@ def get_form_controls(self, sheet: str) -> List[FormControl]:
33563356
return arr
33573357
raise RuntimeError(err)
33583358

3359+
def get_hyperlink_cells(self, sheet: str, link_type: str) -> List[str]:
3360+
"""
3361+
Returns cell references which contain hyperlinks in a given worksheet
3362+
name and link type. The optional parameter `link_type` use for specific
3363+
link type,the optional values are `External` for website links,
3364+
`Location` for moving to one of cell in this workbook, `None` for no
3365+
links. If `link_type` is empty, it will return all hyperlinks in the
3366+
worksheet.
3367+
3368+
Args:
3369+
sheet (str): The worksheet name
3370+
link_type (str): The hyperlink type to filter
3371+
3372+
Returns:
3373+
List[str]: Return the cell name list if no error occurred,
3374+
otherwise raise a RuntimeError with the message.
3375+
"""
3376+
prepare_args(
3377+
[sheet, link_type],
3378+
[argsRule("sheet", [str]), argsRule("link_type", [str])],
3379+
)
3380+
lib.GetHyperLinkCells.restype = types_go._StringArrayErrorResult
3381+
res = lib.GetHyperLinkCells(
3382+
self.file_index,
3383+
sheet.encode(ENCODE),
3384+
link_type.encode(ENCODE),
3385+
)
3386+
arr = c_value_to_py(res, StringArrayErrorResult()).arr
3387+
err = res.Err.decode(ENCODE)
3388+
if not err:
3389+
return arr if arr else []
3390+
raise RuntimeError(err)
3391+
33593392
def get_merge_cells(self, sheet: str, *without_values: bool) -> List[MergeCell]:
33603393
"""
33613394
Get all merged cells from a specific worksheet. If the `without_values`
@@ -3793,6 +3826,27 @@ def get_sheet_props(self, sheet: str) -> Optional[SheetPropsOptions]:
37933826
return c_value_to_py(res.opts, SheetPropsOptions())
37943827
raise RuntimeError(err)
37953828

3829+
def get_sheet_protection(self, sheet: str) -> Optional[SheetProtectionOptions]:
3830+
"""
3831+
Get worksheet protection settings by given worksheet name. Note that the
3832+
password in the returned will always be empty.
3833+
3834+
Args:
3835+
sheet (str): The sheet name.
3836+
3837+
Returns:
3838+
Optional[SheetProtectionOptions]: Return the sheet protection
3839+
options if no error occurred, otherwise raise a RuntimeError with
3840+
the message.
3841+
"""
3842+
prepare_args([sheet], [argsRule("sheet", [str])])
3843+
lib.GetSheetProtection.restype = types_go._GetSheetProtectionResult
3844+
res = lib.GetSheetProtection(self.file_index, sheet.encode(ENCODE))
3845+
err = res.err.decode(ENCODE)
3846+
if not err:
3847+
return c_value_to_py(res.opts, SheetProtectionOptions())
3848+
raise RuntimeError(err)
3849+
37963850
def get_sheet_view(self, sheet: str, view_index: int) -> Optional[ViewOptions]:
37973851
"""
37983852
Gets the value of sheet view options. The view_index may be negative and

main.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,8 +1457,8 @@ func GetFormControls(idx int, sheet *C.char) C.struct_GetFormControlsResult {
14571457
return C.struct_GetFormControlsResult{Err: C.CString(err.Error())}
14581458
}
14591459
cArray := C.malloc(C.size_t(len(opts)) * C.size_t(unsafe.Sizeof(C.struct_FormControl{})))
1460-
for i, dn := range opts {
1461-
cVal, err := goValueToC(reflect.ValueOf(dn), reflect.ValueOf(&C.struct_FormControl{}))
1460+
for i, opt := range opts {
1461+
cVal, err := goValueToC(reflect.ValueOf(opt), reflect.ValueOf(&C.struct_FormControl{}))
14621462
if err != nil {
14631463
return C.struct_GetFormControlsResult{Err: C.CString(err.Error())}
14641464
}
@@ -1467,6 +1467,29 @@ func GetFormControls(idx int, sheet *C.char) C.struct_GetFormControlsResult {
14671467
return C.struct_GetFormControlsResult{FormControlsLen: C.int(len(opts)), FormControls: (*C.struct_FormControl)(cArray), Err: C.CString(emptyString)}
14681468
}
14691469

1470+
// GetHyperLinkCells returns cell references which contain hyperlinks in a
1471+
// given worksheet name and link type. The optional parameter 'linkType' use for
1472+
// specific link type,the optional values are "External" for website links,
1473+
// "Location" for moving to one of cell in this workbook, "None" for no links.
1474+
// If linkType is empty, it will return all hyperlinks in the worksheet.
1475+
//
1476+
//export GetHyperLinkCells
1477+
func GetHyperLinkCells(idx int, sheet, linkType *C.char) C.struct_StringArrayErrorResult {
1478+
f, ok := files.Load(idx)
1479+
if !ok {
1480+
return C.struct_StringArrayErrorResult{Err: C.CString(errFilePtr)}
1481+
}
1482+
result, err := f.(*excelize.File).GetHyperLinkCells(C.GoString(sheet), C.GoString(linkType))
1483+
if err != nil {
1484+
return C.struct_StringArrayErrorResult{Err: C.CString(err.Error())}
1485+
}
1486+
cArray := C.malloc(C.size_t(len(result)) * C.size_t(unsafe.Sizeof(uintptr(0))))
1487+
for i, v := range result {
1488+
*(*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(cArray)) + uintptr(i)*unsafe.Sizeof(uintptr(0)))) = unsafe.Pointer(C.CString(v))
1489+
}
1490+
return C.struct_StringArrayErrorResult{ArrLen: C.int(len(result)), Arr: (**C.char)(cArray), Err: C.CString(emptyString)}
1491+
}
1492+
14701493
// GetMergeCells provides a function to get all merged cells from a specific
14711494
// worksheet. If the `withoutValues` parameter is set to true, it will not
14721495
// return the cell values of merged cells, only the range reference will be
@@ -1794,6 +1817,27 @@ func GetSheetProps(idx int, sheet *C.char) C.struct_GetSheetPropsResult {
17941817
return C.struct_GetSheetPropsResult{opts: cVal.Elem().Interface().(C.struct_SheetPropsOptions), err: C.CString(emptyString)}
17951818
}
17961819

1820+
// GetSheetProtection provides a function to get worksheet protection settings
1821+
// by given worksheet name. Note that the password in the returned will always
1822+
// be empty.
1823+
//
1824+
//export GetSheetProtection
1825+
func GetSheetProtection(idx int, sheet *C.char) C.struct_GetSheetProtectionResult {
1826+
f, ok := files.Load(idx)
1827+
if !ok {
1828+
return C.struct_GetSheetProtectionResult{err: C.CString(errFilePtr)}
1829+
}
1830+
opts, err := f.(*excelize.File).GetSheetProtection(C.GoString(sheet))
1831+
if err != nil {
1832+
return C.struct_GetSheetProtectionResult{err: C.CString(err.Error())}
1833+
}
1834+
cVal, err := goValueToC(reflect.ValueOf(opts), reflect.ValueOf(&C.struct_SheetProtectionOptions{}))
1835+
if err != nil {
1836+
return C.struct_GetSheetProtectionResult{err: C.CString(err.Error())}
1837+
}
1838+
return C.struct_GetSheetProtectionResult{opts: cVal.Elem().Interface().(C.struct_SheetProtectionOptions), err: C.CString(emptyString)}
1839+
}
1840+
17971841
// GetSheetView gets the value of sheet view options. The viewIndex may be
17981842
// negative and if so is counted backward (-1 is the last view).
17991843
//

test_excelize.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,27 @@ def test_style(self):
872872
"expected type Options for argument 'opts', but got int",
873873
)
874874

875+
opts = excelize.SheetProtectionOptions(
876+
select_locked_cells=True,
877+
select_unlocked_cells=True,
878+
edit_scenarios=True,
879+
)
880+
self.assertIsNone(f.protect_sheet("Sheet1", opts))
881+
result = f.get_sheet_protection("Sheet1")
882+
self.assertEqual(opts, result)
883+
884+
with self.assertRaises(RuntimeError) as context:
885+
f.get_sheet_protection("SheetN")
886+
self.assertEqual(str(context.exception), "sheet SheetN does not exist")
887+
with self.assertRaises(TypeError) as context:
888+
f.get_sheet_protection(1)
889+
self.assertEqual(
890+
str(context.exception),
891+
"expected type str for argument 'sheet', but got int",
892+
)
893+
894+
self.assertIsNone(f.unprotect_sheet("Sheet1"))
895+
875896
self.assertIsNone(
876897
f.protect_sheet(
877898
"Sheet1",
@@ -907,7 +928,6 @@ def test_style(self):
907928
"expected type str for argument 'password', but got int",
908929
)
909930
self.assertIsNone(f.unprotect_sheet("Sheet1", "password"))
910-
self.assertIsNone(f.unprotect_sheet("Sheet1"))
911931

912932
self.assertIsNone(
913933
f.protect_workbook(
@@ -1034,6 +1054,10 @@ def test_none_file_pointer(self):
10341054
with self.assertRaises(RuntimeError) as context:
10351055
f.get_form_controls("Sheet1")
10361056
self.assertEqual(str(context.exception), expected)
1057+
self.assertEqual(str(context.exception), expected)
1058+
with self.assertRaises(RuntimeError) as context:
1059+
f.get_hyperlink_cells("Sheet1", "")
1060+
self.assertEqual(str(context.exception), expected)
10371061
with self.assertRaises(RuntimeError) as context:
10381062
f.get_page_layout("Sheet1")
10391063
self.assertEqual(str(context.exception), expected)
@@ -1055,6 +1079,9 @@ def test_none_file_pointer(self):
10551079
str(context.exception),
10561080
"expected type int for argument 'sheet', but got str",
10571081
)
1082+
with self.assertRaises(RuntimeError) as context:
1083+
f.get_sheet_protection("Sheet1")
1084+
self.assertEqual(str(context.exception), expected)
10581085
with self.assertRaises(RuntimeError) as context:
10591086
f.get_workbook_props()
10601087
self.assertEqual(str(context.exception), expected)
@@ -2185,6 +2212,10 @@ def test_cell_hyperlink(self):
21852212
self.assertTrue(link)
21862213
self.assertEqual(target, display)
21872214

2215+
self.assertEqual(["A3"], f.get_hyperlink_cells("Sheet1", ""))
2216+
self.assertEqual([], f.get_hyperlink_cells("Sheet1", "Location"))
2217+
self.assertEqual([], f.get_hyperlink_cells("Sheet1", "None"))
2218+
21882219
with self.assertRaises(RuntimeError) as context:
21892220
_, _ = f.get_cell_hyperlink("SheetN", "A3")
21902221
self.assertEqual(str(context.exception), "sheet SheetN does not exist")
@@ -2196,6 +2227,17 @@ def test_cell_hyperlink(self):
21962227
"expected type str for argument 'cell', but got int",
21972228
)
21982229

2230+
with self.assertRaises(RuntimeError) as context:
2231+
f.get_hyperlink_cells("SheetN", "")
2232+
self.assertEqual(str(context.exception), "sheet SheetN does not exist")
2233+
2234+
with self.assertRaises(TypeError) as context:
2235+
f.get_hyperlink_cells("Sheet1", 1)
2236+
self.assertEqual(
2237+
str(context.exception),
2238+
"expected type str for argument 'link_type', but got int",
2239+
)
2240+
21992241
self.assertIsNone(f.save_as(os.path.join("test", "TestCellHyperLink.xlsx")))
22002242
self.assertIsNone(f.close())
22012243

types_c.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,12 @@ struct GetSheetPropsResult
912912
char *err;
913913
};
914914

915+
struct GetSheetProtectionResult
916+
{
917+
struct SheetProtectionOptions opts;
918+
char *err;
919+
};
920+
915921
struct GetSheetMapResult
916922
{
917923
int ArrLen;

types_go.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,13 @@ class _GetSheetPropsResult(Structure):
896896
]
897897

898898

899+
class _GetSheetProtectionResult(Structure):
900+
_fields_ = [
901+
("opts", _SheetProtectionOptions),
902+
("err", c_char_p),
903+
]
904+
905+
899906
class _GetSheetMapResult(Structure):
900907
_fields_ = [
901908
("ArrLen", c_int),

0 commit comments

Comments
 (0)