Skip to content

Commit 24efa58

Browse files
committed
encoding: improve UTF-8 decode strategy
1 parent 463d7f4 commit 24efa58

14 files changed

Lines changed: 134 additions & 52 deletions

pygit2/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@
365365
from .settings import Settings
366366
from .submodules import Submodule
367367
from .transaction import ReferenceTransaction
368-
from .utils import to_bytes, to_str
368+
from .utils import decode_fs_path, encode_fs_path, to_bytes, to_str
369369

370370
# Features
371371
features = enums.Feature(C.git_libgit2_features())
@@ -430,15 +430,15 @@ def init_repository(
430430
options.mode = mode
431431

432432
if workdir_path:
433-
workdir_path_ref = ffi.new('char []', to_bytes(workdir_path))
433+
workdir_path_ref = ffi.new('char []', encode_fs_path(workdir_path))
434434
options.workdir_path = workdir_path_ref
435435

436436
if description:
437437
description_ref = ffi.new('char []', to_bytes(description))
438438
options.description = description_ref
439439

440440
if template_path:
441-
template_path_ref = ffi.new('char []', to_bytes(template_path))
441+
template_path_ref = ffi.new('char []', encode_fs_path(template_path))
442442
options.template_path = template_path_ref
443443

444444
if initial_head:
@@ -451,7 +451,7 @@ def init_repository(
451451

452452
# Call
453453
crepository = ffi.new('git_repository **')
454-
err = C.git_repository_init_ext(crepository, to_bytes(path), options)
454+
err = C.git_repository_init_ext(crepository, encode_fs_path(path), options)
455455
check_error(err)
456456

457457
# Ok
@@ -536,7 +536,7 @@ def clone_repository(
536536
with git_fetch_options(payload, opts=opts.fetch_opts):
537537
with git_proxy_options(payload, opts.fetch_opts.proxy_opts, proxy):
538538
crepo = ffi.new('git_repository **')
539-
err = C.git_clone(crepo, to_bytes(url), to_bytes(path), opts)
539+
err = C.git_clone(crepo, to_bytes(url), encode_fs_path(path), opts)
540540
payload.check_error(err)
541541

542542
# Ok

pygit2/blame.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
# Import from pygit2
3232
from .ffi import C, ffi
33-
from .utils import GenericIterator
33+
from .utils import GenericIterator, maybe_string
3434

3535
if TYPE_CHECKING:
3636
from ._libgit2.ffi import GitBlameC, GitHunkC, GitSignatureC
@@ -110,7 +110,7 @@ def orig_path(self) -> None | str:
110110
if not path:
111111
return None
112112

113-
return ffi.string(path).decode('utf-8')
113+
return maybe_string(path)
114114

115115

116116
class Blame:

pygit2/callbacks.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,14 @@
7474
from .enums import CheckoutNotify, CheckoutStrategy, CredentialType, StashApplyProgress
7575
from .errors import Passthrough, check_error
7676
from .ffi import C, ffi
77-
from .utils import StrArray, maybe_string, ptr_to_bytes, to_bytes
77+
from .utils import (
78+
StrArray,
79+
decode_fs_path,
80+
encode_fs_path,
81+
maybe_string,
82+
ptr_to_bytes,
83+
to_bytes,
84+
)
7885

7986
_Credentials = Username | UserPass | Keypair
8087

@@ -784,13 +791,13 @@ def get_credentials(fn, url, username, allowed):
784791
def _checkout_notify_cb(
785792
why, path_cstr, baseline, target, workdir, data: CheckoutCallbacks
786793
):
787-
pypath = maybe_string(path_cstr)
794+
pypath = decode_fs_path(path_cstr)
788795
pybaseline = DiffFile.from_c(ptr_to_bytes(baseline))
789796
pytarget = DiffFile.from_c(ptr_to_bytes(target))
790797
pyworkdir = DiffFile.from_c(ptr_to_bytes(workdir))
791798

792799
try:
793-
data.checkout_notify(why, pypath, pybaseline, pytarget, pyworkdir) # type: ignore[arg-type]
800+
data.checkout_notify(why, pypath, pybaseline, pytarget, pyworkdir)
794801
except Passthrough:
795802
# Unlike most other operations with optional callbacks, checkout
796803
# doesn't support the GIT_PASSTHROUGH return code, so we must bypass
@@ -805,7 +812,7 @@ def _checkout_notify_cb(
805812

806813
@libgit2_callback_void
807814
def _checkout_progress_cb(path, completed_steps, total_steps, data: CheckoutCallbacks):
808-
data.checkout_progress(maybe_string(path), completed_steps, total_steps) # type: ignore[arg-type]
815+
data.checkout_progress(decode_fs_path(path), completed_steps, total_steps)
809816

810817

811818
def _git_checkout_options(
@@ -839,7 +846,7 @@ def _git_checkout_options(
839846
opts.checkout_strategy = int(strategy)
840847

841848
if directory:
842-
target_dir = ffi.new('char[]', to_bytes(directory))
849+
target_dir = ffi.new('char[]', encode_fs_path(directory))
843850
refs.append(target_dir)
844851
opts.target_directory = target_dir
845852

pygit2/config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
# Import from pygit2
3636
from .errors import check_error
3737
from .ffi import C, ffi
38-
from .utils import to_bytes
38+
from .utils import encode_fs_path, to_bytes
3939

4040
if TYPE_CHECKING:
4141
from ._libgit2.ffi import GitConfigC, GitConfigEntryC
@@ -116,7 +116,7 @@ def __init__(self, path: PathLike | str | None = None) -> None:
116116
if not path:
117117
err = C.git_config_new(cconfig)
118118
else:
119-
path_bytes = to_bytes(path)
119+
path_bytes = encode_fs_path(path)
120120
err = C.git_config_open_ondisk(cconfig, path_bytes)
121121

122122
check_error(err, io=True)
@@ -282,7 +282,7 @@ def add_file(self, path: str | PathLike, level: int = 0, force: int = 0) -> None
282282
"""Add a config file instance to an existing config."""
283283

284284
err = C.git_config_add_file_ondisk(
285-
self._config, to_bytes(path), level, ffi.NULL, force
285+
self._config, encode_fs_path(path), level, ffi.NULL, force
286286
)
287287
check_error(err)
288288

pygit2/filter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from ._pygit2 import Blob, FilterSource
3333
from .errors import check_error
3434
from .ffi import C, ffi
35-
from .utils import to_bytes
35+
from .utils import encode_fs_path, to_bytes
3636

3737
if TYPE_CHECKING:
3838
from ._libgit2.ffi import GitFilterListC
@@ -178,7 +178,7 @@ def apply_to_file(self, repo: BaseRepository, path: str) -> bytes:
178178
Return the filtered contents.
179179
"""
180180
buf = ffi.new('git_buf *')
181-
c_path = to_bytes(path)
181+
c_path = encode_fs_path(path)
182182
err = C.git_filter_list_apply_to_file(buf, self._pointer, repo._repo, c_path)
183183
check_error(err)
184184
try:

pygit2/index.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from .enums import DiffOption, FileMode
3434
from .errors import check_error
3535
from .ffi import C, ffi
36-
from .utils import GenericIterator, StrArray, to_bytes, to_str
36+
from .utils import GenericIterator, StrArray, decode_fs_path, encode_fs_path
3737

3838
if typing.TYPE_CHECKING:
3939
from .repository import Repository
@@ -52,7 +52,7 @@ def __init__(self, path: str | PathLike[str] | None = None) -> None:
5252
to read from and write to.
5353
"""
5454
cindex = ffi.new('git_index **')
55-
err = C.git_index_open(cindex, to_bytes(path))
55+
err = C.git_index_open(cindex, encode_fs_path(path))
5656
check_error(err)
5757

5858
self._repo = None
@@ -79,7 +79,7 @@ def __len__(self) -> int:
7979
return C.git_index_entrycount(self._index)
8080

8181
def __contains__(self, path) -> bool:
82-
err = C.git_index_find(ffi.NULL, self._index, to_bytes(path))
82+
err = C.git_index_find(ffi.NULL, self._index, encode_fs_path(path))
8383
if err == C.GIT_ENOTFOUND:
8484
return False
8585

@@ -89,7 +89,7 @@ def __contains__(self, path) -> bool:
8989
def __getitem__(self, key: str | int | PathLike[str]) -> 'IndexEntry':
9090
centry = ffi.NULL
9191
if isinstance(key, str) or hasattr(key, '__fspath__'):
92-
centry = C.git_index_get_bypath(self._index, to_bytes(key), 0)
92+
centry = C.git_index_get_bypath(self._index, encode_fs_path(key), 0)
9393
elif isinstance(key, int):
9494
if key >= 0:
9595
centry = C.git_index_get_byindex(self._index, key)
@@ -180,12 +180,12 @@ def write_tree(self, repo: 'Repository | None' = None) -> Oid:
180180

181181
def remove(self, path: PathLike[str] | str, level: int = 0) -> None:
182182
"""Remove an entry from the Index."""
183-
err = C.git_index_remove(self._index, to_bytes(path), level)
183+
err = C.git_index_remove(self._index, encode_fs_path(path), level)
184184
check_error(err, io=True)
185185

186186
def remove_directory(self, path: PathLike[str] | str, level: int = 0) -> None:
187187
"""Remove a directory from the Index."""
188-
err = C.git_index_remove_directory(self._index, to_bytes(path), level)
188+
err = C.git_index_remove_directory(self._index, encode_fs_path(path), level)
189189
check_error(err, io=True)
190190

191191
def remove_all(self, pathspecs: typing.Sequence[str | PathLike[str]]) -> None:
@@ -221,7 +221,7 @@ def add(self, path_or_entry: 'IndexEntry | str | PathLike[str]') -> None:
221221
err = C.git_index_add(self._index, centry)
222222
elif isinstance(path_or_entry, str) or hasattr(path_or_entry, '__fspath__'):
223223
path = path_or_entry
224-
err = C.git_index_add_bypath(self._index, to_bytes(path))
224+
err = C.git_index_add_bypath(self._index, encode_fs_path(path))
225225
else:
226226
raise TypeError('argument must be string, Path or IndexEntry')
227227

@@ -420,7 +420,7 @@ def _from_c(cls, centry):
420420
return None
421421

422422
automergeable = centry.automergeable != 0
423-
path = to_str(ffi.string(centry.path)) if centry.path else None
423+
path = decode_fs_path(ffi.string(centry.path)) if centry.path else None
424424
mode = FileMode(centry.mode)
425425
contents = ffi.string(centry.ptr, centry.len).decode('utf-8')
426426

@@ -475,7 +475,7 @@ def _to_c(self) -> tuple['ffi.GitIndexEntryC', 'ffi.ArrayC[ffi.char]']:
475475
# basically memcpy()
476476
ffi.buffer(ffi.addressof(centry, 'id'))[:] = self.id.raw[:]
477477
centry.mode = int(self.mode)
478-
path = ffi.new('char[]', to_bytes(self.path))
478+
path = ffi.new('char[]', encode_fs_path(self.path))
479479
centry.path = path
480480

481481
return centry, path
@@ -486,7 +486,7 @@ def _from_c(cls, centry):
486486
return None
487487

488488
entry = cls.__new__(cls)
489-
entry.path = to_str(ffi.string(centry.path))
489+
entry.path = decode_fs_path(ffi.string(centry.path))
490490
entry.mode = FileMode(centry.mode)
491491
entry.id = Oid(raw=bytes(ffi.buffer(ffi.addressof(centry, 'id'))[:]))
492492

@@ -503,7 +503,7 @@ def __getitem__(self, path):
503503
ctheirs = ffi.new('git_index_entry **')
504504

505505
err = C.git_index_conflict_get(
506-
cancestor, cours, ctheirs, self._index._index, to_bytes(path)
506+
cancestor, cours, ctheirs, self._index._index, encode_fs_path(path)
507507
)
508508
check_error(err)
509509

@@ -514,7 +514,7 @@ def __getitem__(self, path):
514514
return ancestor, ours, theirs
515515

516516
def __delitem__(self, path):
517-
err = C.git_index_conflict_remove(self._index._index, to_bytes(path))
517+
err = C.git_index_conflict_remove(self._index._index, encode_fs_path(path))
518518
check_error(err)
519519

520520
def __iter__(self):
@@ -526,7 +526,7 @@ def __contains__(self, path):
526526
ctheirs = ffi.new('git_index_entry **')
527527

528528
err = C.git_index_conflict_get(
529-
cancestor, cours, ctheirs, self._index._index, to_bytes(path)
529+
cancestor, cours, ctheirs, self._index._index, encode_fs_path(path)
530530
)
531531
if err == C.GIT_ENOTFOUND:
532532
return False

pygit2/options.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
from .errors import check_error
3636
from .ffi import C, ffi
37-
from .utils import to_bytes, to_str
37+
from .utils import decode_fs_path, encode_fs_path, to_bytes, to_str
3838

3939
if TYPE_CHECKING:
4040
from ._libgit2.ffi import NULL_TYPE, ArrayC, char, char_pointer
@@ -348,7 +348,7 @@ def option(option_type: Option, arg1: Any = NOT_PASSED, arg2: Any = NOT_PASSED)
348348

349349
try:
350350
if buf.ptr != ffi.NULL:
351-
result = to_str(ffi.string(buf.ptr))
351+
result = decode_fs_path(ffi.string(buf.ptr))
352352
else:
353353
result = None
354354
finally:
@@ -366,7 +366,7 @@ def option(option_type: Option, arg1: Any = NOT_PASSED, arg2: Any = NOT_PASSED)
366366
if path is None:
367367
path_cdata = ffi.NULL
368368
else:
369-
path_bytes = to_bytes(path)
369+
path_bytes = encode_fs_path(path)
370370
path_cdata = ffi.new('char[]', path_bytes)
371371

372372
err = C.git_libgit2_opts(option_type, ffi.cast('int', level), path_cdata)
@@ -423,14 +423,14 @@ def option(option_type: Option, arg1: Any = NOT_PASSED, arg2: Any = NOT_PASSED)
423423
if cert_file is None:
424424
cert_file_cdata = ffi.NULL
425425
else:
426-
cert_file_bytes = to_bytes(cert_file)
426+
cert_file_bytes = encode_fs_path(cert_file)
427427
cert_file_cdata = ffi.new('char[]', cert_file_bytes)
428428

429429
cert_dir_cdata: ArrayC[char] | NULL_TYPE
430430
if cert_dir is None:
431431
cert_dir_cdata = ffi.NULL
432432
else:
433-
cert_dir_bytes = to_bytes(cert_dir)
433+
cert_dir_bytes = encode_fs_path(cert_dir)
434434
cert_dir_cdata = ffi.new('char[]', cert_dir_bytes)
435435

436436
err = C.git_libgit2_opts(option_type, cert_file_cdata, cert_dir_cdata)
@@ -476,7 +476,7 @@ def option(option_type: Option, arg1: Any = NOT_PASSED, arg2: Any = NOT_PASSED)
476476

477477
try:
478478
if buf.ptr != ffi.NULL:
479-
result = to_str(ffi.string(buf.ptr))
479+
result = decode_fs_path(ffi.string(buf.ptr))
480480
else:
481481
result = None
482482
finally:
@@ -492,7 +492,7 @@ def option(option_type: Option, arg1: Any = NOT_PASSED, arg2: Any = NOT_PASSED)
492492
if path is None:
493493
template_path_cdata = ffi.NULL
494494
else:
495-
path_bytes = to_bytes(path)
495+
path_bytes = encode_fs_path(path)
496496
template_path_cdata = ffi.new('char[]', path_bytes)
497497

498498
err = C.git_libgit2_opts(option_type, template_path_cdata)
@@ -688,7 +688,7 @@ def option(option_type: Option, arg1: Any = NOT_PASSED, arg2: Any = NOT_PASSED)
688688

689689
try:
690690
if buf.ptr != ffi.NULL:
691-
result = to_str(ffi.string(buf.ptr))
691+
result = decode_fs_path(ffi.string(buf.ptr))
692692
else:
693693
result = None
694694
finally:
@@ -705,7 +705,7 @@ def option(option_type: Option, arg1: Any = NOT_PASSED, arg2: Any = NOT_PASSED)
705705
if path is None:
706706
homedir_cdata = ffi.NULL
707707
else:
708-
path_bytes = to_bytes(path)
708+
path_bytes = encode_fs_path(path)
709709
homedir_cdata = ffi.new('char[]', path_bytes)
710710

711711
err = C.git_libgit2_opts(option_type, homedir_cdata)

pygit2/packbuilder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
# Import from pygit2
3030
from .errors import check_error
3131
from .ffi import C, ffi
32-
from .utils import to_bytes
32+
from .utils import encode_fs_path
3333

3434
if TYPE_CHECKING:
3535
from pygit2 import Oid, Repository
@@ -76,7 +76,7 @@ def set_threads(self, n_threads: int) -> int:
7676
return C.git_packbuilder_set_threads(self._packbuilder, n_threads)
7777

7878
def write(self, path: str | bytes | PathLike[str] | None = None) -> None:
79-
path_bytes = ffi.NULL if path is None else to_bytes(path)
79+
path_bytes = ffi.NULL if path is None else encode_fs_path(path)
8080
err = C.git_packbuilder_write(
8181
self._packbuilder, path_bytes, 0, ffi.NULL, ffi.NULL
8282
)

pygit2/repository.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
from .remotes import RemoteCollection
8282
from .submodules import SubmoduleCollection
8383
from .transaction import ReferenceTransaction
84-
from .utils import StrArray, maybe_string, to_bytes
84+
from .utils import StrArray, decode_fs_path, encode_fs_path, maybe_string, to_bytes
8585

8686
if TYPE_CHECKING:
8787
from pygit2._libgit2.ffi import (
@@ -219,7 +219,7 @@ def hashfile(
219219
If this is `None` and the `path` parameter is a file within the
220220
repository's working directory, then the `path` will be used.
221221
"""
222-
c_path = to_bytes(path)
222+
c_path = encode_fs_path(path)
223223

224224
c_as_path: ffi.NULL_TYPE | bytes
225225
if as_path is None:
@@ -1824,7 +1824,7 @@ def __init__(
18241824
if hasattr(path, '__fspath__'):
18251825
path = path.__fspath__()
18261826
if not isinstance(path, str):
1827-
path = path.decode('utf-8')
1827+
path = decode_fs_path(path)
18281828
path_backend = init_file_backend(path, int(flags))
18291829
super().__init__(path_backend)
18301830
else:

0 commit comments

Comments
 (0)