@@ -10,6 +10,8 @@ This module provides :class:`Program` for compiling source code into
1010from __future__ import annotations
1111
1212from dataclasses import dataclass
13+ from os import PathLike
14+ from pathlib import Path
1315import threading
1416from typing import TYPE_CHECKING
1517from warnings import warn
@@ -294,6 +296,12 @@ cdef class Program:
294296class ProgramOptions:
295297 """Customizable options for configuring :class:`Program`.
296298
299+ Every path-valued option (``include_path``, ``pre_include``, ``create_pch``,
300+ ``use_pch``, ``pch_dir``, ``fdevice_time_trace``) accepts either a :class:`str`
301+ or any :class:`os.PathLike`, and stores it as a :class:`pathlib.Path`. Callers
302+ building paths with :mod:`pathlib` therefore never need to convert back to
303+ ``str``.
304+
297305 Attributes
298306 ----------
299307 name : str , optional
@@ -359,11 +367,11 @@ class ProgramOptions:
359367 undefine_macro : Union[str , list[str]], optional
360368 Cancel any previous definition of a macro , or list of macros.
361369 Default: None
362- include_path : Union[str , list[str]], optional
370+ include_path : Union[str , os. PathLike , list[Union[ str , os.PathLike] ]], optional
363371 Add the directory or directories to the list of directories to be searched for headers.
364372 Default: None
365- pre_include : Union[str , list[str]], optional
366- Preinclude one or more headers during preprocessing. Can be either a string or a list of strings .
373+ pre_include : Union[str , os. PathLike , list[Union[ str , os.PathLike] ]], optional
374+ Preinclude one or more headers during preprocessing. Can be either a single path or a list of paths .
367375 Default: None
368376 no_source_include : bool , optional
369377 Disable the default behavior of adding the directory of each input source to the include path.
@@ -422,7 +430,7 @@ class ProgramOptions:
422430 no_cache : bool , optional
423431 Disable compiler caching.
424432 Default: False
425- fdevice_time_trace : str , optional
433+ fdevice_time_trace : Union[ str , os.PathLike] , optional
426434 Generate time trace JSON for profiling compilation (NVRTC only ).
427435 Default: None
428436 device_float128 : bool , optional
@@ -437,13 +445,13 @@ class ProgramOptions:
437445 pch : bool , optional
438446 Use default precompiled header (NVRTC only , CUDA 12.8+).
439447 Default: False
440- create_pch : str , optional
448+ create_pch : Union[ str , os.PathLike] , optional
441449 Create precompiled header file (NVRTC only , CUDA 12.8+).
442450 Default: None
443- use_pch : str , optional
451+ use_pch : Union[ str , os.PathLike] , optional
444452 Use specific precompiled header file (NVRTC only , CUDA 12.8+).
445453 Default: None
446- pch_dir : str , optional
454+ pch_dir : Union[ str , os.PathLike] , optional
447455 PCH directory location (NVRTC only , CUDA 12.8+).
448456 Default: None
449457 pch_verbose : bool , optional
@@ -486,8 +494,8 @@ class ProgramOptions:
486494 gen_opt_lto: bool | None = None
487495 define_macro: str | tuple[str , str] | list[str | tuple[str , str]] | tuple[str | tuple[str , str], ...] | None = None
488496 undefine_macro: str | list[str] | tuple[str] | None = None
489- include_path: str | list[str] | tuple[str] | None = None
490- pre_include: str | list[str] | tuple[str] | None = None
497+ include_path: str | PathLike[str] | list[str | PathLike[str]] | tuple[str | PathLike[str] ] | None = None
498+ pre_include: str | PathLike[str] | list[str | PathLike[str]] | tuple[str | PathLike[str] ] | None = None
491499 no_source_include: bool | None = None
492500 std: str | None = None
493501 builtin_move_forward: bool | None = None
@@ -507,14 +515,14 @@ class ProgramOptions:
507515 fdevice_syntax_only: bool | None = None
508516 minimal: bool | None = None
509517 no_cache: bool | None = None
510- fdevice_time_trace: str | None = None
518+ fdevice_time_trace: str | PathLike[str] | None = None
511519 device_float128: bool | None = None
512520 frandom_seed: str | None = None
513521 ofast_compile: str | None = None
514522 pch: bool | None = None
515- create_pch: str | None = None
516- use_pch: str | None = None
517- pch_dir: str | None = None
523+ create_pch: str | PathLike[str] | None = None
524+ use_pch: str | PathLike[str] | None = None
525+ pch_dir: str | PathLike[str] | None = None
518526 pch_verbose: bool | None = None
519527 pch_messages: bool | None = None
520528 instantiate_templates_in_pch: bool | None = None
@@ -524,6 +532,12 @@ class ProgramOptions:
524532
525533 def __post_init__(self ) -> None:
526534 self._name = self .name.encode()
535+ # Path-valued options accept str or os.PathLike; normalize to Path so
536+ # callers never have to convert back to str just to build options.
537+ for _field in _PATH_OPTION_FIELDS:
538+ _value = getattr (self , _field)
539+ if _value is not None:
540+ setattr(self , _field , _coerce_path_option(_value ))
527541 # Set arch to default if not provided
528542 if self.arch is None:
529543 self.arch = f" sm_{Device().arch}"
@@ -628,6 +642,42 @@ class ProgramOptions:
628642# =============================================================================
629643
630644
645+ # ``ProgramOptions`` fields that name a filesystem path. ``str`` and
646+ # ``os.PathLike`` values for these are normalized to :class:`pathlib.Path` in
647+ # ``ProgramOptions.__post_init__`` so everything downstream sees one type.
648+ #
649+ # ``name`` is not here: NVRTC uses it as the source *filename*, but it is a
650+ # plain label (``ProgramOptions.__post_init__`` encodes it, and the program
651+ # cache inspects it for a directory component), so it stays ``str``. ``time``
652+ # is not here either: NVRTC treats it as an output filename, but the same
653+ # field is forwarded to ``LinkerOptions.time`` (a bool flag) for PTX inputs.
654+ _PATH_OPTION_FIELDS = (
655+ " include_path" ,
656+ " pre_include" ,
657+ " create_pch" ,
658+ " use_pch" ,
659+ " pch_dir" ,
660+ " fdevice_time_trace" ,
661+ )
662+
663+
664+ def _coerce_path_option (value ):
665+ """ Normalize a path-valued :class:`ProgramOptions` field.
666+
667+ ``str`` / :class:`os.PathLike` becomes :class:`pathlib.Path`; a ``list``
668+ or ``tuple`` has its ``str`` / :class:`os.PathLike` items converted while
669+ keeping the container type. Anything else is returned unchanged, so the
670+ "silently ignored at compile time" behavior of non-path values (``False``,
671+ ``range(...)``, ...) is unaffected.
672+ """
673+ if isinstance (value, (str , PathLike)):
674+ return Path(value)
675+ if isinstance (value, (list , tuple )):
676+ coerced = [Path(v) if isinstance (v, (str , PathLike)) else v for v in value]
677+ return tuple (coerced) if isinstance (value, tuple ) else coerced
678+ return value
679+
680+
631681def _program_compile_uncached (program , target_type , name_expressions , logs ):
632682 """ Run ``Program_compile`` without the cache wrapper.
633683
@@ -1125,13 +1175,15 @@ cdef inline list _prepare_nvrtc_options_impl(object opts):
11251175 for macro in opts.undefine_macro:
11261176 options.append(f" --undefine-macro={macro}" )
11271177 if opts.include_path is not None :
1128- if isinstance (opts.include_path, str ):
1178+ # ``__post_init__`` turns str/PathLike into Path, but the dataclass is
1179+ # mutable, so accept either form here.
1180+ if isinstance (opts.include_path, (str , PathLike)):
11291181 options.append(f" --include-path={opts.include_path}" )
11301182 elif is_sequence(opts.include_path):
11311183 for path in opts.include_path:
11321184 options.append(f" --include-path={path}" )
11331185 if opts.pre_include is not None :
1134- if isinstance (opts.pre_include, str ):
1186+ if isinstance (opts.pre_include, ( str , PathLike) ):
11351187 options.append(f" --pre-include={opts.pre_include}" )
11361188 elif is_sequence(opts.pre_include):
11371189 for header in opts.pre_include:
0 commit comments