__sitecustomize____sitecustomize__ directories__sitecustomize__ discovery__sitecustomize__pth files__sitecustomize__sitecustomize.py andusercustomize.pyThis PEP proposes supporting extensible customization of the interpreter byallowing users to install files that will be executed at startup.
PEP 648 was rejectedby the steering councilas it has a limited number of use cases and further complicates the startup sequence.
System administrators, tools that repackage the interpreter and somelibraries need to customize aspects of the interpreter at startup time.
This is usually achieved viasitecustomize.py for system administratorswhilst libraries rely on exploitingpth files. This PEP proposes a way ofachieving the same functionality in a more user-friendly and structured way.
pth filesIf a library needs to perform any customization before an import or thatrelates to the general working of the interpreter, they often rely on thefact thatpth files, which are loaded at startup and implemented via thesite module[7], can include Python code that will be executed when thepth file is evaluated.
Note thatpth files were originally developed to just add additionaldirectories tosys.path, but they may also contain lines which startwith “import”, which will be passed toexec(). Users have exploited thisfeature to allow the customizations that they needed. See setuptools[4] or betterexceptions[5] as examples.
Usingpth files for this purpose is far from ideal for library developers,as they need to inject code into a single line preceded by an import, makingit rather unreadable. Library developers following that practice will usuallycreate a module that performs all actions on import, as done bybetterexceptions[5], but the approach is still not reallyuser friendly.
Additionally, it is also non-ideal for users of the interpreter if they wantto inspect what is being executed at Python startup as they need to reviewall thepth files for potential code execution which can be spread acrossall site paths. Most of thosepth files will be “legitimate”pthfiles that just modify the path, answering the question of “what is changingmy interpreter at startup” a rather complex one.
Lastly, there have been multiple suggestions for removing code execution frompth files, see[1] and[2].
sitecustomize.pyWhilst sitecustomize is an acceptable solution, it assumes a single person isin charge of the system and the interpreter. If both the system administratorand the responsibility of provisioning the interpreter want to addcustomizations at the interpreter startup they need to agree on the contentsof the file and combine all the changes. This is not a major limitationthough, and it is not the main driver of this change. Should the changehappen, it will also improve the situation for these users, as rather thanhaving asitecustomize.py which performs all those actions, they can havecustom isolated files named after the features they want to enhance. As anexample, Ubuntu could change their currentsitecustomize.py to just beubuntu_apport_python_hook. This not only better represents its intent butalso gives users of the interpreter a better understanding of themodifications happening on their interpreter.
This PEP proposes supporting extensible customization of the interpreter atstartup by executing all files discovered in directories named__sitecustomize__ in sitepackages[8] orusersitepackages[9] at startup time.
__sitecustomize__The name aims to follow the already existing concept ofsitecustomize.py.As the directory will be withinsys.path, given that it is located insite paths, we choose to use double underscore around its name, to preventcolliding with the already existingsitecustomize.py.
__sitecustomize__ directoriesThe Python interpreter will look at startup for directory named__sitecustomize__ within any of the standard site-packages path.
These are commonly the Python system location and the user location, but areultimately defined by the site module logic.
Users can usesite.sitepackages[8] andsite.usersitepackages[9] to know the paths wherethe interpreter can discover__sitecustomize__ directories.
__sitecustomize__ discoveryThe__sitecustomize__ directories will be discovered exactly afterpthfiles are discovered in a site-packages path as part ofsite.addsitedir[10].
These is repeated for each of the site-packages path in the exact same orderthat is being followed today forpth files.
__sitecustomize__The implementation will execute the files within__sitecustomize__ bysorting them by name when discovering each of the__sitecustomize__directories. We discourage users to rely on the order of execution though.
We considered executing them in random order, but that could result indifferent results depending on how the interpreter chooses to pick up thosefiles. So even if it won’t be a good practice to rely on other files beingexecuted, we think that is better than having randomly different results oninterpreter startup. We chose to run the files after thepth files incase a user needs to add items to the path before running a files.
pth filespth files can be used to add paths intosys.path, but this should notaffect the__sitecustomize__ discovery process, as those directories arelooked up exclusively in site-packages paths.
__sitecustomize__When a__sitecustomize__ directory is discovered, all of the files thathave a.py extension within it will be read withio.open_code andexecuted by usingexec[11].
An empty dictionary will be passed asglobals to theexec functionto prevent unexpected interactions between different files.
Any error on the execution of any of the files will not be logged unless theinterpreter is run in verbose mode and it should not stop the evaluation ofother files. The user will receive a message in stderr saying that the filefailed to be executed and that verbose mode can be used to get moreinformation. This behaviour mimics the one existing forsitecustomize.py.
The customizations applied to an interpreter via the new__sitecustomize__ solutions will continue to work when a user creates avirtual environment the same way thatsitecustomize.pyinteract with virtual environments.
This is a difference when compared topth files, which are not propagatedinto virtual environments unlessinclude-system-site-packages is enabled.
If library maintainers have features installed via__sitecustomize__ thatthey do not want to propagate into virtual environments, they should detectif they are running within a virtual environment by checkingsys.prefix==sys.base_prefix. This behavior is similar to packages that modify the globalsitecustomize.py.
sitecustomize.py andusercustomize.pyUntil removed,sitecustomize andusercustomize will be executed after__sitecustomize__ similar to pth files. See the Backward compatibilitysection for information on removal plans forsitecustomize andusercustomize.
To facilitate debugging of the Python startup, if the site module is invokedit will print the__sitecustomize__ directories that will be discoveredon startup.
Packages will be encouraged to include the name of the package within thename of the file to avoid collisions between packages. But the onlyrequirement on the filename is that it ends in.py for the interpreter toexecute them.
In some scenarios, like when the startup time is key, it might be desired todisable this option altogether. The already existing flag-S[3]will disable allsite-related manipulation, including this new feature.If the flag is passed in,__sitecustomize__ directories will not bediscovered.
Additionally, to allow for starting the interpreter disabling only this newfeature a new option will be added under-X:disablesitecustomize,which will disable the discovery of__sitecustomize__ exclusively.
Lastly, the user can disable the discovery of__sitecustomize__directories only in the user site by disabling the user site via any of themultiple options in thesite.py module.
Whilst build backends can choose to provide an option to facilitate theinstallation of these files into a__sitecustomize__ directory, thisPEP does not address that directly. Similar topth files, build backendscan choose to not provide an easy-to-configure mechanism for__sitecustomize__ files and let users hook into the installationprocess to include such files. We do not think build backends enhancedsupport as a requirement for this PEP.
A concern in this implementation is how Python interpreter startup time canbe affected by this addition. We expect the performance impact to be highlycoupled to the logic in the files that a user or sysadmin installs in thePython environment being tested.
If the interpreter has any files in their__sitecustomize__ directory,the file execution time plus a call reading the code will be added to thestartup time. This is similar to how code execution is impacting startup timethroughsitecustomize.py,usercustomize.py and code inpth files.We will therefore focus here on comparing this solution against those three,as otherwise the actual time added to startup is highly dependent on the codethat is being executed in those files.
Results were gathered by running “./python.exe -c pass” with perf on 50iterations, repeating 50 times the command on each iteration and getting thegeometric mean of all the results. The file used to run those benchmarks ischecked in in the reference implementation[6].
The benchmark was run with 3.10 alpha 7 compiled with PGO and LTO with thefollowing parameters and system state:
The code placed to be executed inpth files,sitecustomize.py,usercustomize.py and files within__sitecustomize__ is the following:
import time; x = time.time() ** 5
The file is aimed at execution a simple operation but still expected to benegligible. This is to put the experiment in a situation where we makevisible any hit on performance due to the mechanism whilst still making itrelatively realistic. Additionally, it starts with an import and is a singleline to be able to be used inpth files.
| Test | # of files | Time (us) | ||||
|---|---|---|---|---|---|---|
| # | sitecustomize.py | usercustomize.py | pth | __sitecustomize__ | Run 1 | Run 2 |
| 1 | 0 | 0 | 0 | Dir not created | 13884 | 13897 |
| 2 | 0 | 0 | 0 | 0 | 13871 | 13818 |
| 3 | 0 | 0 | 1 | 0 | 13964 | 13924 |
| 4 | 0 | 0 | 0 | 1 | 13940 | 13939 |
| 5 | 1 | 1 | 0 | 0 | 13990 | 13993 |
| 6 | 0 | 0 | 0 | 2 (system + user) | 14063 | 14040 |
| 7 | 0 | 0 | 50 | 0 | 16011 | 16014 |
| 8 | 0 | 0 | 0 | 50 | 15456 | 15448 |
Results can be reproduced withrun-benchmark.py script provided in thereference implementation[6].
We interpret the following from these results:
__sitecustomize__ scripts compared tositecustomize.pyandusercustomize.py slows down the interpreter by 0.3%. We expect thisslowdown untilsitecustomize.py andusercustomize.py are removed ina future release as even if the user does not create the files, theinterpreter will still attempt to import them.__sitecustomize__ produces a speedup of ~3.5% in startup. Which is likelyrelated to the simpler logic to evaluate__sitecustomize__ files comparedtopth file execution.A new audit event will be added and triggered on__sitecustomize__execution to facilitate security inspection by callingsys.audit[12] with “sitecustimze.exec_file” as name and the filename asargument.
This PEP aims to move all code execution frompth files to files within a__sitecustomize__ directory. We think this is an improvement to system adminsfor the following reasons:
pth files.__sitecustomize__ directory, potentially allowing users to install onlypackages that does not change the interpreter startup.In short, whilst this allows for a malicious users to drop a file that willbe executed at startup, it’s an improvement compared to the existingpthfiles.
This can be documented and taught as simple as saying that the interpreterwill try to look for the__sitecustomize__ directory at startup in itssite paths and if it finds any files with.py extension, it will thenexecute it one by one.
For system administrators and tools that package the interpreter, we can nowrecommend placing files in__sitecustomize__ as they used to placesitecustomize.py. Being more comfortable on that their content won’t beoverridden by the next person, as they can provide with specific files tohandle the logic they want to customize.
Library developers should be able to specify a new argument on tools likesetuptools that will inject those new files. Something likesitecustomize_files=["scripts/betterexceptions.py"], which allows them toadd those. Should the build backend not support that, they can manuallyinstall them as they used to do withpth files. We will recommend them toinclude the name of the package as part of the file’s name.
This PEP adds a deprecation warning onsitecustomize.py,usercustomize.py andpth code execution in 3.11, 3.12 and 3.13. Withplans on removing those features by 3.14. The migration from those solutionsto__sitecustomize__ should ideally be just moving the logic into adifferent file.
Whilst the existingsitecustomize.py mechanism was created targetingSystem Administrators that placed it in a site path, the file could beactually placed anywhere in the path at the time that the interpreter wasstarting up. The new mechanism does not allow for users to place__sitecustomize__ directories anywhere in the path, but only in sitepaths. System administrators can recover a similar behavior tositecustomize.py by adding a custom file in__sitecustomize__ whichjust importssitecustomize as a migration path.
An initial implementation that passes the CPython test suite is available forevaluation[6].
This implementation is just for the reviewer to play with and check potentialissues that this PEP could generate.
Whilst the current status “works” it presents the issues listed in themotivation. After analyzing the impact of this change, we believe it is worthit, given the enhanced experience it brings.
pth filesAnother option would be to just glorify and document the usage ofpth filesto inject code at startup code, but that is a suboptimal experience for usersas listed in the motivation.
__sitecustomize__ a namespace packageWe considered making the directory a namespace package and just import allthe modules within it, which allowed searching across all paths insys.path at initialization time and provided a way to declaredependencies between files by importing each other. This was rejected formultiple reasons:
__init__.py file in one of the locations.__sitecustomize__ as we are looking forpth files already in the site paths compared to performing an actualimport of a namespace package.init.d users might be tempted to implement this feature in a way that userscould also add code at shutdown, but extra support for that is not needed, asPython users can already do that viaatexit.
We considered extending the use of entry points to allow specifying filesthat should be executed at startup but we discarded that solution due to twomain reasons. The first one being impact on startup time. This approach willrequire scanning all packages distribution information to just execute ahandful of files. This has an impact on performance even if the user is notusing the feature and such impact growths linearly with the number of packagesinstalled in the environment. The second reason was that the proposedimplementation in this PEP offers a single solution for startup customizationfor packages and system administrators. Additionally, if the main objective ofentry points is to make it easy for libraries to install files at startup,that can still be added and make the build backends just install the fileswithin the__sitecustomize__ directory.
This document is placed in the public domain or under the CC0-1.0-Universallicense, whichever is more permissive.
Thanks Pablo Galindo for contributing to this PEP and offering his PC to runthe benchmark.
Source:https://github.com/python/peps/blob/main/peps/pep-0648.rst
Last modified:2025-11-07 04:32:09 GMT