1# SPDX-FileCopyrightText: 2025 James R. Barlow
2# SPDX-License-Identifier: MPL-2.0
3
4
5from __future__ import annotations
6
7# Conversion mode API
8from contextlib import contextmanager
9from typing import Literal
10
11from pikepdf import _core
12
13
14def set_object_conversion_mode(mode: Literal['implicit', 'explicit']) -> None:
15 """Set global object conversion mode.
16
17 This controls how PDF scalar values (integers, booleans, reals) are
18 returned when accessing PDF objects.
19
20 Args:
21 mode: Conversion mode.
22 - ``'implicit'`` (default): Automatically convert PDF integers to
23 Python ``int``, booleans to ``bool``, and reals to ``Decimal``.
24 This is the legacy behavior.
25 - ``'explicit'``: Return PDF scalars as ``pikepdf.Integer``,
26 ``pikepdf.Boolean``, and ``pikepdf.Real`` objects. This enables
27 better type safety and static type checking.
28
29 Example:
30 >>> pikepdf.set_object_conversion_mode('explicit')
31 >>> pdf = pikepdf.open('test.pdf')
32 >>> count = pdf.Root.Count
33 >>> isinstance(count, pikepdf.Integer) # True in explicit mode
34 True
35 >>> int(count) # Convert to Python int
36 5
37
38 .. versionadded:: 10.1
39 """
40 _core._set_explicit_conversion_mode(mode == 'explicit')
41
42
43def get_object_conversion_mode() -> Literal['implicit', 'explicit']:
44 """Get current effective object conversion mode.
45
46 This returns the mode that is currently in effect for the calling thread,
47 taking into account both the global setting and any active
48 :func:`explicit_conversion` context managers.
49
50 Returns:
51 The current effective conversion mode: ``'implicit'`` or ``'explicit'``.
52
53 .. versionadded:: 10.1
54
55 .. versionchanged:: 10.2
56 Now returns the effective mode, including thread-local context manager state.
57 """
58 return 'explicit' if _core._get_effective_explicit_mode() else 'implicit'
59
60
61@contextmanager
62def explicit_conversion():
63 """Context manager for explicit conversion mode.
64
65 Within this context, PDF scalar values will be returned as
66 ``pikepdf.Integer``, ``pikepdf.Boolean``, and ``pikepdf.Real`` objects
67 instead of being automatically converted to Python native types.
68
69 This context manager is thread-local: it only affects the current thread
70 and takes precedence over the global setting from
71 :func:`set_object_conversion_mode`. Nested context managers are supported.
72
73 Example:
74 >>> with pikepdf.explicit_conversion():
75 ... pdf = pikepdf.open('test.pdf')
76 ... count = pdf.Root.Count
77 ... isinstance(count, pikepdf.Integer)
78 True
79
80 .. versionadded:: 10.1
81
82 .. versionchanged:: 10.2
83 Now thread-local and takes precedence over global setting.
84 """
85 _core._enter_thread_explicit_mode()
86 try:
87 yield
88 finally:
89 _core._exit_thread_explicit_mode()