Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-jupyter-core for openSUSE:Factory checked in at 2021-10-25 15:17:03 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-jupyter-core (Old) and /work/SRC/openSUSE:Factory/.python-jupyter-core.new.1890 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-jupyter-core" Mon Oct 25 15:17:03 2021 rev:7 rq:925907 version:4.8.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-jupyter-core/python-jupyter-core.changes 2021-02-11 12:48:14.093562912 +0100 +++ /work/SRC/openSUSE:Factory/.python-jupyter-core.new.1890/python-jupyter-core.changes 2021-10-25 15:17:35.385681498 +0200 @@ -1,0 +2,20 @@ +Sun Oct 17 17:28:20 UTC 2021 - Ben Greiner <c...@bnavigator.de> + +- Update to version 4.8.1 + * Print an error message instead of an exception when a command + is not found (PR #218) + * Return canonical path when using %APPDATA% on Windows (PR #222) + * Print full usage on missing or invalid commands (PR #225) + * Remove dependency on pywin32 package on PyPy (PR #230) + * Update packages listed in jupyter --version (PR #232) + * Inherit base aliases/flags from traitlets Application, + including --show-config from traitlets 5 (PR #233) + * Trigger warning when trying to check hidden file status on PyPy + (PR #238) + +------------------------------------------------------------------- +Tue Oct 5 15:35:48 UTC 2021 - Stefan Schubert <sch...@suse.de> + +- Use libalternatives instead of update-alternatives. + +------------------------------------------------------------------- Old: ---- jupyter_core-4.7.1.tar.gz New: ---- jupyter_core-4.8.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-jupyter-core.spec ++++++ --- /var/tmp/diff_new_pack.1CGsPR/_old 2021-10-25 15:17:35.933681841 +0200 +++ /var/tmp/diff_new_pack.1CGsPR/_new 2021-10-25 15:17:35.937681843 +0200 @@ -1,5 +1,5 @@ # -# spec file for package python-jupyter-core +# spec file # # Copyright (c) 2021 SUSE LLC # @@ -16,6 +16,13 @@ # +# +%if 0%{?suse_version} > 1500 +%bcond_without libalternatives +%else +%bcond_with libalternatives +%endif + %global flavor @BUILD_FLAVOR@%{nil} %if "%{flavor}" == "test" %define psuffix -test @@ -24,11 +31,10 @@ %define psuffix %{nil} %bcond_with test %endif -%bcond_without python2 %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-jupyter-core%{psuffix} -Version: 4.7.1 +Version: 4.8.1 Release: 0 Summary: Base package on which Jupyter projects rely License: BSD-3-Clause @@ -40,11 +46,16 @@ BuildRequires: %{python_module traitlets} BuildRequires: fdupes BuildRequires: jupyter-jupyter_core-filesystem -BuildRequires: python-rpm-macros +BuildRequires: python-rpm-macros >= 20210929 Requires: jupyter-jupyter_core-filesystem Requires: python-traitlets +%if %{with libalternatives} +BuildRequires: alts +Requires: alts +%else Requires(post): update-alternatives -Requires(postun): update-alternatives +Requires(postun):update-alternatives +%endif Recommends: python-ipython Provides: python-jupyter_core = %{version}-%{release} Obsoletes: python-jupyter_core < %{version}-%{release} @@ -57,6 +68,7 @@ BuildArch: noarch %if %{with test} BuildRequires: %{python_module jupyter-core} +BuildRequires: %{python_module pytest-timeout} BuildRequires: %{python_module pytest} %endif %python_subpackages @@ -101,6 +113,11 @@ popd %endif +%pre +# removing old update-alternatives entries +# If libalternatives is used: Removing old update-alternatives entries. +%python_libalternatives_reset_alternative jupyter + %post %python_install_alternative jupyter jupyter-migrate jupyter-troubleshoot ++++++ jupyter_core-4.7.1.tar.gz -> jupyter_core-4.8.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/PKG-INFO new/jupyter_core-4.8.1/PKG-INFO --- old/jupyter_core-4.7.1/PKG-INFO 2021-01-31 19:30:07.000000000 +0100 +++ new/jupyter_core-4.8.1/PKG-INFO 2021-09-17 03:21:05.841862400 +0200 @@ -1,13 +1,13 @@ Metadata-Version: 2.1 Name: jupyter_core -Version: 4.7.1 +Version: 4.8.1 Summary: Jupyter core package. A base package on which Jupyter projects rely. Home-page: https://jupyter.org Author: Jupyter Development Team Author-email: jupy...@googlegroups.org -License: BSD -Description: There is no reason to install this package on its own. +License: BSD-3-Clause Platform: UNKNOWN +Classifier: Framework :: Jupyter Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: Intended Audience :: Science/Research @@ -16,3 +16,7 @@ Classifier: Programming Language :: Python :: 3 Requires-Python: >=3.6 Description-Content-Type: text/plain +License-File: COPYING.md + +There is no reason to install this package on its own. + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/dev-requirements.txt new/jupyter_core-4.8.1/dev-requirements.txt --- old/jupyter_core-4.7.1/dev-requirements.txt 2020-11-02 16:52:35.000000000 +0100 +++ new/jupyter_core-4.8.1/dev-requirements.txt 2021-09-17 02:11:12.000000000 +0200 @@ -1,2 +1,4 @@ -pytest ipykernel +pytest +pytest-cov +pytest-timeout Binary files old/jupyter_core-4.7.1/docs/.DS_Store and new/jupyter_core-4.8.1/docs/.DS_Store differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/docs/changelog.rst new/jupyter_core-4.8.1/docs/changelog.rst --- old/jupyter_core-4.7.1/docs/changelog.rst 2021-01-31 19:25:32.000000000 +0100 +++ new/jupyter_core-4.8.1/docs/changelog.rst 2021-09-17 02:11:12.000000000 +0200 @@ -1,6 +1,28 @@ Changes in jupyter-core ======================= +4.8 +--- + +4.8.0 +~~~~~ + +`on +GitHub <https://github.com/jupyter/jupyter_core/releases/tag/4.8.0>`__ + +See the `jupyter_core +4.8 <https://github.com/jupyter/jupyter_core/milestone/20?closed=1>`__ +milestone on GitHub for the full list of pull requests and issues closed. + +jupyter-core now has experimental support for PyPy (Python 3.7). Some features are known not to work due to limitations in PyPy, such as hidden file detection on Windows. + +- Print an error message instead of an exception when a command is not found (:ghpull:`218`) +- Return canonical path when using ``%APPDATA%`` on Windows (:ghpull:`222`) +- Print full usage on missing or invalid commands (:ghpull:`225`) +- Remove dependency on ``pywin32`` package on PyPy (:ghpull:`230`) +- Update packages listed in ``jupyter --version`` (:ghpull:`232`) +- Inherit base aliases/flags from traitlets Application, including ``--show-config`` from traitlets 5 (:ghpull:`233`) +- Trigger warning when trying to check hidden file status on PyPy (:ghpull:`238`) 4.7 --- @@ -17,7 +39,7 @@ ~~~~~ `on -GitHub <https://github.com/jupyter/jupyter_core/releases/tag/4.7>`__ +GitHub <https://github.com/jupyter/jupyter_core/releases/tag/4.7.0>`__ See the `jupyter_core 4.7 <https://github.com/jupyter/jupyter_core/milestone/19?closed=1>`__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/jupyter_core/application.py new/jupyter_core-4.8.1/jupyter_core/application.py --- old/jupyter_core-4.7.1/jupyter_core/application.py 2020-11-02 16:52:35.000000000 +0100 +++ new/jupyter_core-4.8.1/jupyter_core/application.py 2021-09-17 02:11:12.000000000 +0200 @@ -27,12 +27,21 @@ # aliases and flags -base_aliases = { +base_aliases = {} +if isinstance(Application.aliases, dict): + # traitlets 5 + base_aliases.update(Application.aliases) +_jupyter_aliases = { 'log-level' : 'Application.log_level', 'config' : 'JupyterApp.config_file', } +base_aliases.update(_jupyter_aliases) -base_flags = { +base_flags = {} +if isinstance(Application.flags, dict): + # traitlets 5 + base_flags.update(Application.flags) +_jupyter_flags = { 'debug': ({'Application' : {'log_level' : logging.DEBUG}}, "set log level to logging.DEBUG (maximize logging output)"), 'generate-config': ({'JupyterApp': {'generate_config': True}}, @@ -40,6 +49,7 @@ 'y': ({'JupyterApp': {'answer_yes': True}}, "Answer yes to any questions instead of prompting."), } +base_flags.update(_jupyter_flags) class NoStart(Exception): """Exception to raise when an application shouldn't start""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/jupyter_core/command.py new/jupyter_core-4.8.1/jupyter_core/command.py --- old/jupyter_core-4.7.1/jupyter_core/command.py 2020-11-02 16:52:35.000000000 +0100 +++ new/jupyter_core-4.8.1/jupyter_core/command.py 2021-09-17 02:11:12.000000000 +0200 @@ -37,10 +37,10 @@ parser = JupyterParser( description="Jupyter: Interactive Computing", ) - group = parser.add_mutually_exclusive_group(required=True) + group = parser.add_mutually_exclusive_group(required=False) # don't use argparse's version action because it prints to stderr on py2 group.add_argument('--version', action='store_true', - help="show the jupyter command's version and exit") + help="show the versions of core jupyter packages and exit") group.add_argument('subcommand', type=str, nargs='?', help='the subcommand to launch') group.add_argument('--config-dir', action='store_true', @@ -122,12 +122,12 @@ abs_path = which(jupyter_subcommand, path=search_path) if abs_path is None: raise Exception( - 'Jupyter command `{}` not found.'.format(jupyter_subcommand) + '\nJupyter command `{}` not found.'.format(jupyter_subcommand) ) if not os.access(abs_path, os.X_OK): raise Exception( - 'Jupyter command `{}` is not executable.'.format(jupyter_subcommand) + '\nJupyter command `{}` is not executable.'.format(jupyter_subcommand) ) return abs_path @@ -184,26 +184,31 @@ args, opts = parser.parse_known_args() subcommand = args.subcommand if args.version: - print('{:<17}:'.format('jupyter core'), __version__) - for package, name in [ - ('notebook', 'jupyter-notebook'), - ('qtconsole', 'qtconsole'), - ('IPython', 'ipython'), - ('ipykernel', 'ipykernel'), - ('jupyter_client', 'jupyter client'), - ('jupyterlab', 'jupyter lab'), - ('nbconvert', 'nbconvert'), - ('ipywidgets', 'ipywidgets'), - ('nbformat', 'nbformat'), - ('traitlets', 'traitlets'), + print("Selected Jupyter core packages...") + for package in [ + 'IPython', + 'ipykernel', + 'ipywidgets', + 'jupyter_client', + 'jupyter_core', + 'jupyter_server', + 'jupyterlab', + 'nbclient', + 'nbconvert', + 'nbformat', + 'notebook', + 'qtconsole', + 'traitlets', ]: - version = None try: - mod = __import__(package) - version = mod.__version__ + if package == 'jupyter_core': # We're already here + version = __version__ + else: + mod = __import__(package) + version = mod.__version__ except ImportError: version = 'not installed' - print('{:<17}:'.format(name), version) + print('{:<17}:'.format(package), version) return if args.json and not args.paths: sys.exit("--json is only used with --paths") @@ -279,10 +284,17 @@ return if not subcommand: - parser.print_usage(file=sys.stderr) - sys.exit("subcommand is required") + parser.print_help(file=sys.stderr) + sys.exit("\nPlease specify a subcommand or one of the optional arguments.") - command = _jupyter_abspath(subcommand) + try: + command = _jupyter_abspath(subcommand) + except Exception as e: + parser.print_help(file=sys.stderr) + # special-case alias of "jupyter help" to "jupyter --help" + if subcommand == "help": + return + sys.exit(e) try: _execvp(command, sys.argv[1:]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/jupyter_core/paths.py new/jupyter_core-4.8.1/jupyter_core/paths.py --- old/jupyter_core-4.7.1/jupyter_core/paths.py 2021-01-31 17:51:47.000000000 +0100 +++ new/jupyter_core-4.8.1/jupyter_core/paths.py 2021-09-17 02:11:12.000000000 +0200 @@ -14,6 +14,7 @@ import errno import tempfile import warnings +from pathlib import Path from contextlib import contextmanager @@ -37,7 +38,7 @@ homedir = os.path.expanduser('~') # Next line will make things work even when /home/ is a symlink to # /usr/home as it is on FreeBSD, for example - homedir = os.path.realpath(homedir) + homedir = str(Path(homedir).resolve()) return homedir _dtemps = {} @@ -90,7 +91,7 @@ elif os.name == 'nt': appdata = os.environ.get('APPDATA', None) if appdata: - return pjoin(appdata, 'jupyter') + return str(Path(appdata, 'jupyter').resolve()) else: return pjoin(jupyter_config_dir(), 'data') else: @@ -267,8 +268,15 @@ return False raise - if stat_res.st_file_attributes & stat.FILE_ATTRIBUTE_HIDDEN: - return True + try: + if stat_res.st_file_attributes & stat.FILE_ATTRIBUTE_HIDDEN: + return True + except AttributeError: + # allow AttributeError on PyPy for Windows + # 'stat_result' object has no attribute 'st_file_attributes' + # https://foss.heptapod.net/pypy/pypy/-/issues/3469 + warnings.warn("hidden files are not detectable on this system, so no file will be marked as hidden.") + pass return False @@ -384,7 +392,11 @@ fname : unicode The path to the file to secure """ - import win32api + try: + import win32api + except ImportError: + return _win32_restrict_file_to_user_ctypes(fname) + import win32security import ntsecuritycon as con @@ -403,6 +415,400 @@ win32security.SetFileSecurity(fname, win32security.DACL_SECURITY_INFORMATION, sd) +def _win32_restrict_file_to_user_ctypes(fname): + """Secure a windows file to read-only access for the user. + + Follows guidance from win32 library creator: + http://timgolden.me.uk/python/win32_how_do_i/add-security-to-a-file.html + + This method should be executed against an already generated file which + has no secrets written to it yet. + + Parameters + ---------- + + fname : unicode + The path to the file to secure + """ + import ctypes + from ctypes import wintypes + + advapi32 = ctypes.WinDLL('advapi32', use_last_error=True) + secur32 = ctypes.WinDLL('secur32', use_last_error=True) + + NameSamCompatible = 2 + WinBuiltinAdministratorsSid = 26 + DACL_SECURITY_INFORMATION = 4 + ACL_REVISION = 2 + ERROR_INSUFFICIENT_BUFFER = 122 + ERROR_MORE_DATA = 234 + + SYNCHRONIZE = 0x100000 + DELETE = 0x00010000 + STANDARD_RIGHTS_REQUIRED = 0xF0000 + STANDARD_RIGHTS_READ = 0x20000 + STANDARD_RIGHTS_WRITE = 0x20000 + FILE_READ_DATA = 1 + FILE_READ_EA = 8 + FILE_READ_ATTRIBUTES = 128 + FILE_WRITE_DATA = 2 + FILE_APPEND_DATA = 4 + FILE_WRITE_EA = 16 + FILE_WRITE_ATTRIBUTES = 256 + FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF + FILE_GENERIC_READ = ( + STANDARD_RIGHTS_READ + | FILE_READ_DATA + | FILE_READ_ATTRIBUTES + | FILE_READ_EA + | SYNCHRONIZE + ) + FILE_GENERIC_WRITE = ( + STANDARD_RIGHTS_WRITE + | FILE_WRITE_DATA + | FILE_WRITE_ATTRIBUTES + | FILE_WRITE_EA + | FILE_APPEND_DATA + | SYNCHRONIZE + ) + + class ACL(ctypes.Structure): + _fields_ = [ + ('AclRevision', wintypes.BYTE), + ('Sbz1', wintypes.BYTE), + ('AclSize', wintypes.WORD), + ('AceCount', wintypes.WORD), + ('Sbz2', wintypes.WORD), + ] + + PSID = ctypes.c_void_p + PACL = ctypes.POINTER(ACL) + PSECURITY_DESCRIPTOR = ctypes.POINTER(wintypes.BYTE) + + def _nonzero_success(result, func, args): + if not result: + raise ctypes.WinError(ctypes.get_last_error()) + return args + + secur32.GetUserNameExW.errcheck = _nonzero_success + secur32.GetUserNameExW.restype = wintypes.BOOL + secur32.GetUserNameExW.argtypes = ( + ctypes.c_int, # EXTENDED_NAME_FORMAT NameFormat + wintypes.LPWSTR, # LPWSTR lpNameBuffer, + wintypes.PULONG, # PULONG nSize + ) + + advapi32.CreateWellKnownSid.errcheck = _nonzero_success + advapi32.CreateWellKnownSid.restype = wintypes.BOOL + advapi32.CreateWellKnownSid.argtypes = ( + wintypes.DWORD, # WELL_KNOWN_SID_TYPE WellKnownSidType + PSID, # PSID DomainSid + PSID, # PSID pSid + wintypes.PDWORD, # DWORD *cbSid + ) + + advapi32.LookupAccountNameW.errcheck = _nonzero_success + advapi32.LookupAccountNameW.restype = wintypes.BOOL + advapi32.LookupAccountNameW.argtypes = ( + wintypes.LPWSTR, # LPCWSTR lpSystemName + wintypes.LPWSTR, # LPCWSTR lpAccountName + PSID, # PSID Sid + wintypes.LPDWORD, # LPDWORD cbSid + wintypes.LPWSTR, # LPCWSTR ReferencedDomainName + wintypes.LPDWORD, # LPDWORD cchReferencedDomainName + wintypes.LPDWORD, # PSID_NAME_USE peUse + ) + + advapi32.AddAccessAllowedAce.errcheck = _nonzero_success + advapi32.AddAccessAllowedAce.restype = wintypes.BOOL + advapi32.AddAccessAllowedAce.argtypes = ( + PACL, # PACL pAcl + wintypes.DWORD, # DWORD dwAceRevision + wintypes.DWORD, # DWORD AccessMask + PSID, # PSID pSid + ) + + advapi32.SetSecurityDescriptorDacl.errcheck = _nonzero_success + advapi32.SetSecurityDescriptorDacl.restype = wintypes.BOOL + advapi32.SetSecurityDescriptorDacl.argtypes = ( + PSECURITY_DESCRIPTOR, # PSECURITY_DESCRIPTOR pSecurityDescriptor + wintypes.BOOL, # BOOL bDaclPresent + PACL, # PACL pDacl + wintypes.BOOL, # BOOL bDaclDefaulted + ) + + advapi32.GetFileSecurityW.errcheck = _nonzero_success + advapi32.GetFileSecurityW.restype = wintypes.BOOL + advapi32.GetFileSecurityW.argtypes = ( + wintypes.LPCWSTR, # LPCWSTR lpFileName + wintypes.DWORD, # SECURITY_INFORMATION RequestedInformation + PSECURITY_DESCRIPTOR, # PSECURITY_DESCRIPTOR pSecurityDescriptor + wintypes.DWORD, # DWORD nLength + wintypes.LPDWORD, # LPDWORD lpnLengthNeeded + ) + + advapi32.SetFileSecurityW.errcheck = _nonzero_success + advapi32.SetFileSecurityW.restype = wintypes.BOOL + advapi32.SetFileSecurityW.argtypes = ( + wintypes.LPCWSTR, # LPCWSTR lpFileName + wintypes.DWORD, # SECURITY_INFORMATION SecurityInformation + PSECURITY_DESCRIPTOR, # PSECURITY_DESCRIPTOR pSecurityDescriptor + ) + + advapi32.MakeAbsoluteSD.errcheck = _nonzero_success + advapi32.MakeAbsoluteSD.restype = wintypes.BOOL + advapi32.MakeAbsoluteSD.argtypes = ( + PSECURITY_DESCRIPTOR, # pSelfRelativeSecurityDescriptor + PSECURITY_DESCRIPTOR, # pAbsoluteSecurityDescriptor + wintypes.LPDWORD, # LPDWORD lpdwAbsoluteSecurityDescriptorSize + PACL, # PACL pDacl + wintypes.LPDWORD, # LPDWORD lpdwDaclSize + PACL, # PACL pSacl + wintypes.LPDWORD, # LPDWORD lpdwSaclSize + PSID, # PSID pOwner + wintypes.LPDWORD, # LPDWORD lpdwOwnerSize + PSID, # PSID pPrimaryGroup + wintypes.LPDWORD, # LPDWORD lpdwPrimaryGroupSize + ) + + advapi32.MakeSelfRelativeSD.errcheck = _nonzero_success + advapi32.MakeSelfRelativeSD.restype = wintypes.BOOL + advapi32.MakeSelfRelativeSD.argtypes = ( + PSECURITY_DESCRIPTOR, # pAbsoluteSecurityDescriptor + PSECURITY_DESCRIPTOR, # pSelfRelativeSecurityDescriptor + wintypes.LPDWORD, # LPDWORD lpdwBufferLength + ) + + advapi32.InitializeAcl.errcheck = _nonzero_success + advapi32.InitializeAcl.restype = wintypes.BOOL + advapi32.InitializeAcl.argtypes = ( + PACL, # PACL pAcl, + wintypes.DWORD, # DWORD nAclLength, + wintypes.DWORD, # DWORD dwAclRevision + ) + + def CreateWellKnownSid(WellKnownSidType): + # return a SID for predefined aliases + pSid = (ctypes.c_char * 1)() + cbSid = wintypes.DWORD() + try: + advapi32.CreateWellKnownSid( + WellKnownSidType, None, pSid, ctypes.byref(cbSid) + ) + except OSError as e: + if e.winerror != ERROR_INSUFFICIENT_BUFFER: + raise + pSid = (ctypes.c_char * cbSid.value)() + advapi32.CreateWellKnownSid( + WellKnownSidType, None, pSid, ctypes.byref(cbSid) + ) + return pSid[:] + + def GetUserNameEx(NameFormat): + # return the user or other security principal associated with + # the calling thread + nSize = ctypes.pointer(ctypes.c_ulong(0)) + try: + secur32.GetUserNameExW(NameFormat, None, nSize) + except WindowsError as e: + if e.winerror != ERROR_MORE_DATA: + raise + if not nSize.contents.value: + return None + lpNameBuffer = ctypes.create_unicode_buffer(nSize.contents.value) + secur32.GetUserNameExW(NameFormat, lpNameBuffer, nSize) + return lpNameBuffer.value + + def LookupAccountName(lpSystemName, lpAccountName): + # return a security identifier (SID) for an account on a system + # and the name of the domain on which the account was found + cbSid = wintypes.DWORD(0) + cchReferencedDomainName = wintypes.DWORD(0) + peUse = wintypes.DWORD(0) + try: + advapi32.LookupAccountNameW( + lpSystemName, + lpAccountName, + None, + ctypes.byref(cbSid), + None, + ctypes.byref(cchReferencedDomainName), + ctypes.byref(peUse), + ) + except WindowsError as e: + if e.winerror != ERROR_INSUFFICIENT_BUFFER: + raise + Sid = ctypes.create_unicode_buffer('', cbSid.value) + pSid = ctypes.cast(ctypes.pointer(Sid), wintypes.LPVOID) + lpReferencedDomainName = ctypes.create_unicode_buffer( + '', cchReferencedDomainName.value + 1 + ) + success = advapi32.LookupAccountNameW( + lpSystemName, + lpAccountName, + pSid, + ctypes.byref(cbSid), + lpReferencedDomainName, + ctypes.byref(cchReferencedDomainName), + ctypes.byref(peUse), + ) + if not success: + raise ctypes.WinError() + return pSid, lpReferencedDomainName.value, peUse.value + + def AddAccessAllowedAce(pAcl, dwAceRevision, AccessMask, pSid): + # add an access-allowed access control entry (ACE) + # to an access control list (ACL) + advapi32.AddAccessAllowedAce(pAcl, dwAceRevision, AccessMask, pSid) + + def GetFileSecurity(lpFileName, RequestedInformation): + # return information about the security of a file or directory + nLength = wintypes.DWORD(0) + try: + advapi32.GetFileSecurityW( + lpFileName, + RequestedInformation, + None, + 0, + ctypes.byref(nLength), + ) + except WindowsError as e: + if e.winerror != ERROR_INSUFFICIENT_BUFFER: + raise + if not nLength.value: + return None + pSecurityDescriptor = (wintypes.BYTE * nLength.value)() + advapi32.GetFileSecurityW( + lpFileName, + RequestedInformation, + pSecurityDescriptor, + nLength, + ctypes.byref(nLength), + ) + return pSecurityDescriptor + + def SetFileSecurity(lpFileName, RequestedInformation, pSecurityDescriptor): + # set the security of a file or directory object + advapi32.SetFileSecurityW( + lpFileName, RequestedInformation, pSecurityDescriptor + ) + + def SetSecurityDescriptorDacl( + pSecurityDescriptor, bDaclPresent, pDacl, bDaclDefaulted + ): + # set information in a discretionary access control list (DACL) + advapi32.SetSecurityDescriptorDacl( + pSecurityDescriptor, bDaclPresent, pDacl, bDaclDefaulted + ) + + def MakeAbsoluteSD(pSelfRelativeSecurityDescriptor): + # return a security descriptor in absolute format + # by using a security descriptor in self-relative format as a template + pAbsoluteSecurityDescriptor = None + lpdwAbsoluteSecurityDescriptorSize = wintypes.DWORD(0) + pDacl = None + lpdwDaclSize = wintypes.DWORD(0) + pSacl = None + lpdwSaclSize = wintypes.DWORD(0) + pOwner = None + lpdwOwnerSize = wintypes.DWORD(0) + pPrimaryGroup = None + lpdwPrimaryGroupSize = wintypes.DWORD(0) + try: + advapi32.MakeAbsoluteSD( + pSelfRelativeSecurityDescriptor, + pAbsoluteSecurityDescriptor, + ctypes.byref(lpdwAbsoluteSecurityDescriptorSize), + pDacl, + ctypes.byref(lpdwDaclSize), + pSacl, + ctypes.byref(lpdwSaclSize), + pOwner, + ctypes.byref(lpdwOwnerSize), + pPrimaryGroup, + ctypes.byref(lpdwPrimaryGroupSize), + ) + except WindowsError as e: + if e.winerror != ERROR_INSUFFICIENT_BUFFER: + raise + pAbsoluteSecurityDescriptor = ( + wintypes.BYTE * lpdwAbsoluteSecurityDescriptorSize.value + )() + pDaclData = (wintypes.BYTE * lpdwDaclSize.value)() + pDacl = ctypes.cast(pDaclData, PACL).contents + pSaclData = (wintypes.BYTE * lpdwSaclSize.value)() + pSacl = ctypes.cast(pSaclData, PACL).contents + pOwnerData = (wintypes.BYTE * lpdwOwnerSize.value)() + pOwner = ctypes.cast(pOwnerData, PSID) + pPrimaryGroupData = (wintypes.BYTE * lpdwPrimaryGroupSize.value)() + pPrimaryGroup = ctypes.cast(pPrimaryGroupData, PSID) + advapi32.MakeAbsoluteSD( + pSelfRelativeSecurityDescriptor, + pAbsoluteSecurityDescriptor, + ctypes.byref(lpdwAbsoluteSecurityDescriptorSize), + pDacl, + ctypes.byref(lpdwDaclSize), + pSacl, + ctypes.byref(lpdwSaclSize), + pOwner, + lpdwOwnerSize, + pPrimaryGroup, + ctypes.byref(lpdwPrimaryGroupSize), + ) + return pAbsoluteSecurityDescriptor + + def MakeSelfRelativeSD(pAbsoluteSecurityDescriptor): + # return a security descriptor in self-relative format + # by using a security descriptor in absolute format as a template + pSelfRelativeSecurityDescriptor = None + lpdwBufferLength = wintypes.DWORD(0) + try: + advapi32.MakeSelfRelativeSD( + pAbsoluteSecurityDescriptor, + pSelfRelativeSecurityDescriptor, + ctypes.byref(lpdwBufferLength), + ) + except WindowsError as e: + if e.winerror != ERROR_INSUFFICIENT_BUFFER: + raise + pSelfRelativeSecurityDescriptor = ( + wintypes.BYTE * lpdwBufferLength.value + )() + advapi32.MakeSelfRelativeSD( + pAbsoluteSecurityDescriptor, + pSelfRelativeSecurityDescriptor, + ctypes.byref(lpdwBufferLength), + ) + return pSelfRelativeSecurityDescriptor + + def NewAcl(): + # return a new, initialized ACL (access control list) structure + nAclLength = 32767 # TODO: calculate this: ctypes.sizeof(ACL) + ? + acl_data = ctypes.create_string_buffer(nAclLength) + pAcl = ctypes.cast(acl_data, PACL).contents + advapi32.InitializeAcl(pAcl, nAclLength, ACL_REVISION) + return pAcl + + SidAdmins = CreateWellKnownSid(WinBuiltinAdministratorsSid) + SidUser = LookupAccountName('', GetUserNameEx(NameSamCompatible))[0] + + Acl = NewAcl() + AddAccessAllowedAce(Acl, ACL_REVISION, FILE_ALL_ACCESS, SidAdmins) + AddAccessAllowedAce( + Acl, + ACL_REVISION, + FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, + SidUser, + ) + + SelfRelativeSD = GetFileSecurity(fname, DACL_SECURITY_INFORMATION) + AbsoluteSD = MakeAbsoluteSD(SelfRelativeSD) + SetSecurityDescriptorDacl(AbsoluteSD, 1, Acl, 0) + SelfRelativeSD = MakeSelfRelativeSD(AbsoluteSD) + + SetFileSecurity(fname, DACL_SECURITY_INFORMATION, SelfRelativeSD) + + def get_file_mode(fname): """Retrieves the file mode corresponding to fname in a filesystem-tolerant manner. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/jupyter_core/tests/mocking.py new/jupyter_core-4.8.1/jupyter_core/tests/mocking.py --- old/jupyter_core-4.7.1/jupyter_core/tests/mocking.py 2020-10-30 15:38:29.000000000 +0100 +++ new/jupyter_core-4.8.1/jupyter_core/tests/mocking.py 2021-09-17 02:11:12.000000000 +0200 @@ -29,8 +29,3 @@ patch.object(os, 'name', 'posix'), patch.object(sys, 'platform', 'linux2'), ) - -windows = MultiPatch( - patch.object(os, 'name', 'nt'), - patch.object(sys, 'platform', 'win32'), -) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/jupyter_core/tests/test_command.py new/jupyter_core-4.8.1/jupyter_core/tests/test_command.py --- old/jupyter_core-4.7.1/jupyter_core/tests/test_command.py 2020-11-02 16:52:35.000000000 +0100 +++ new/jupyter_core-4.8.1/jupyter_core/tests/test_command.py 2021-09-17 02:11:12.000000000 +0200 @@ -4,7 +4,7 @@ import os import sys import sysconfig -from subprocess import check_output, CalledProcessError +from subprocess import check_output, PIPE, CalledProcessError from unittest.mock import patch import pytest @@ -21,7 +21,7 @@ """Get output of a jupyter command""" if not isinstance(cmd, list): cmd = [cmd] - return check_output([sys.executable, '-m', 'jupyter_core'] + cmd).decode('utf8').strip() + return check_output([sys.executable, '-m', 'jupyter_core'] + cmd, stderr=PIPE).decode('utf8').strip() def write_executable(path, source): @@ -109,8 +109,10 @@ def test_subcommand_not_found(): - with pytest.raises(CalledProcessError): + with pytest.raises(CalledProcessError) as excinfo: get_jupyter_output('nonexistant-subcommand') + stderr = excinfo.value.stderr.decode('utf8') + assert 'Jupyter command `jupyter-nonexistant-subcommand` not found.' in stderr @patch.object(sys, 'argv', [__file__] + sys.argv[1:]) def test_subcommand_list(tmpdir): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/jupyter_core/tests/test_paths.py new/jupyter_core-4.8.1/jupyter_core/tests/test_paths.py --- old/jupyter_core-4.7.1/jupyter_core/tests/test_paths.py 2021-01-31 17:51:47.000000000 +0100 +++ new/jupyter_core-4.8.1/jupyter_core/tests/test_paths.py 2021-09-17 03:20:27.000000000 +0200 @@ -10,7 +10,9 @@ import tempfile from unittest.mock import patch import pytest +import subprocess import sys +import warnings from jupyter_core import paths from jupyter_core.paths import ( @@ -19,7 +21,7 @@ secure_write, is_hidden, is_file_hidden ) -from .mocking import darwin, windows, linux +from .mocking import darwin, linux pjoin = os.path.join @@ -36,8 +38,6 @@ 'XDG_RUNTIME_DIR': '', }) -appdata = patch.dict('os.environ', {'APPDATA': 'appdata'}) - no_config_env = patch.dict('os.environ', { 'JUPYTER_CONFIG_DIR': '', 'JUPYTER_DATA_DIR': '', @@ -50,7 +50,7 @@ def realpath(path): - return os.path.realpath(os.path.expanduser(path)) + return os.path.abspath(os.path.realpath(os.path.expanduser(path))) home_jupyter = realpath('~/.jupyter') @@ -65,6 +65,7 @@ assert not paths.envset(f"FOO_{v}") assert not paths.envset("THIS_VARIABLE_SHOULD_NOT_BE_SET") +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_config_dir_darwin(): with darwin, no_config_env: config = jupyter_config_dir() @@ -74,23 +75,24 @@ config = jupyter_config_dir() assert config == jupyter_config_env - +@pytest.mark.skipif(sys.platform != "win32", reason="only run on windows") def test_config_dir_windows(): - with windows, no_config_env: + with no_config_env: config = jupyter_config_dir() assert config == home_jupyter - with windows, config_env: + with config_env: config = jupyter_config_dir() assert config == jupyter_config_env +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_config_dir_linux(): - with windows, no_config_env: + with linux, no_config_env: config = jupyter_config_dir() assert config == home_jupyter - with windows, config_env: + with linux, config_env: config = jupyter_config_dir() assert config == jupyter_config_env @@ -102,6 +104,7 @@ assert data == data_env +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_data_dir_darwin(): with darwin: data = jupyter_data_dir() @@ -112,18 +115,18 @@ data = jupyter_data_dir() assert data == realpath('~/Library/Jupyter') - +@pytest.mark.skipif(sys.platform != "win32", reason="only run on windows") def test_data_dir_windows(): - with windows, appdata: - data = jupyter_data_dir() - assert data == pjoin('appdata', 'jupyter') + data = jupyter_data_dir() + assert data == realpath(pjoin(os.environ.get('APPDATA', None), 'jupyter')) - with windows, appdata, xdg: + with xdg: # windows should ignore xdg data = jupyter_data_dir() - assert data == pjoin('appdata', 'jupyter') + assert data == realpath(pjoin(os.environ.get('APPDATA', None), 'jupyter')) +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_data_dir_linux(): with linux, no_xdg: data = jupyter_data_dir() @@ -141,6 +144,7 @@ assert runtime == rtd_env +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_runtime_dir_darwin(): with darwin: runtime = jupyter_runtime_dir() @@ -151,18 +155,18 @@ runtime = jupyter_runtime_dir() assert runtime == realpath('~/Library/Jupyter/runtime') - +@pytest.mark.skipif(sys.platform != "win32", reason="only run on windows") def test_runtime_dir_windows(): - with windows, appdata: - runtime = jupyter_runtime_dir() - assert runtime == pjoin('appdata', 'jupyter', 'runtime') + runtime = jupyter_runtime_dir() + assert runtime == realpath(pjoin(os.environ.get('APPDATA', None), 'jupyter', 'runtime')) - with windows, appdata, xdg: + with xdg: # windows should ignore xdg runtime = jupyter_runtime_dir() - assert runtime == pjoin('appdata', 'jupyter', 'runtime') + assert runtime == realpath(pjoin(os.environ.get('APPDATA', None), 'jupyter', 'runtime')) +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_runtime_dir_linux(): with linux, no_xdg: runtime = jupyter_runtime_dir() @@ -256,18 +260,53 @@ assert not is_file_hidden(subdir56, os.stat(subdir56)) -@pytest.mark.skipif(sys.platform != "win32", reason="only run on windows") -def test_is_hidden_win32(): +@pytest.mark.skipif( + not (sys.platform == "win32" and (("__pypy__" not in sys.modules) or (sys.implementation.version >= (7, 3, 6)))), + reason="only run on windows/cpython or pypy >= 7.3.6: https://foss.heptapod.net/pypy/pypy/-/issues/3469" +) +def test_is_hidden_win32_cpython(): import ctypes with tempfile.TemporaryDirectory() as root: subdir1 = os.path.join(root, 'subdir') os.makedirs(subdir1) assert not is_hidden(subdir1, root) - r = ctypes.windll.kernel32.SetFileAttributesW(subdir1, 0x02) - print(r) # Helps debugging + subprocess.check_call(["attrib", "+h", subdir1]) assert is_hidden(subdir1, root) assert is_file_hidden(subdir1) +@pytest.mark.skipif( + not (sys.platform == "win32" and "__pypy__" in sys.modules and sys.implementation.version < (7, 3, 6)), + reason="only run on windows/pypy < 7.3.6: https://foss.heptapod.net/pypy/pypy/-/issues/3469" +) +def test_is_hidden_win32_pypy(): + import ctypes + with tempfile.TemporaryDirectory() as root: + subdir1 = os.path.join(root, 'subdir') + os.makedirs(subdir1) + assert not is_hidden(subdir1, root) + subprocess.check_call(["attrib", "+h", subdir1]) + + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Trigger a warning. + assert not is_hidden(subdir1, root) + # Verify the warning was triggered + assert len(w) == 1 + assert issubclass(w[-1].category, UserWarning) + assert "hidden files are not detectable on this system" in str(w[-1].message) + + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Trigger a warning. + assert not is_file_hidden(subdir1) + # Verify the warning was triggered + assert len(w) == 1 + assert issubclass(w[-1].category, UserWarning) + assert "hidden files are not detectable on this system" in str(w[-1].message) + + @pytest.mark.skipif(sys.platform != "win32", reason="only runs on windows") def test_secure_write_win32(): @@ -289,8 +328,7 @@ def check_user_only_permissions(fname): # Windows has it's own permissions ACL patterns - import win32api - username = win32api.GetUserName().lower() + username = os.environ["USERNAME"].lower() permissions = fetch_win32_permissions(fname) print(permissions) # for easier debugging assert username in permissions diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/jupyter_core/version.py new/jupyter_core-4.8.1/jupyter_core/version.py --- old/jupyter_core-4.7.1/jupyter_core/version.py 2021-01-31 19:28:26.000000000 +0100 +++ new/jupyter_core-4.8.1/jupyter_core/version.py 2021-09-17 03:20:43.000000000 +0200 @@ -11,7 +11,7 @@ 'serial' ]) -version_info = VersionInfo(4, 7, 1, 'final', 0) +version_info = VersionInfo(4, 8, 1, 'final', 0) _specifier_ = {'alpha': 'a', 'beta': 'b', 'candidate': 'rc', 'final': '', 'dev': 'dev'} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/jupyter_core.egg-info/PKG-INFO new/jupyter_core-4.8.1/jupyter_core.egg-info/PKG-INFO --- old/jupyter_core-4.7.1/jupyter_core.egg-info/PKG-INFO 2021-01-31 19:30:07.000000000 +0100 +++ new/jupyter_core-4.8.1/jupyter_core.egg-info/PKG-INFO 2021-09-17 03:21:05.000000000 +0200 @@ -1,13 +1,13 @@ Metadata-Version: 2.1 Name: jupyter-core -Version: 4.7.1 +Version: 4.8.1 Summary: Jupyter core package. A base package on which Jupyter projects rely. Home-page: https://jupyter.org Author: Jupyter Development Team Author-email: jupy...@googlegroups.org -License: BSD -Description: There is no reason to install this package on its own. +License: BSD-3-Clause Platform: UNKNOWN +Classifier: Framework :: Jupyter Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: Intended Audience :: Science/Research @@ -16,3 +16,7 @@ Classifier: Programming Language :: Python :: 3 Requires-Python: >=3.6 Description-Content-Type: text/plain +License-File: COPYING.md + +There is no reason to install this package on its own. + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/jupyter_core.egg-info/SOURCES.txt new/jupyter_core-4.8.1/jupyter_core.egg-info/SOURCES.txt --- old/jupyter_core-4.7.1/jupyter_core.egg-info/SOURCES.txt 2021-01-31 19:30:07.000000000 +0100 +++ new/jupyter_core-4.8.1/jupyter_core.egg-info/SOURCES.txt 2021-09-17 03:21:05.000000000 +0200 @@ -6,7 +6,6 @@ jupyter.py setup.cfg setup.py -docs/.DS_Store docs/Makefile docs/changelog.rst docs/conf.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/jupyter_core.egg-info/requires.txt new/jupyter_core-4.8.1/jupyter_core.egg-info/requires.txt --- old/jupyter_core-4.7.1/jupyter_core.egg-info/requires.txt 2021-01-31 19:30:07.000000000 +0100 +++ new/jupyter_core-4.8.1/jupyter_core.egg-info/requires.txt 2021-09-17 03:21:05.000000000 +0200 @@ -1,4 +1,4 @@ traitlets -[:sys_platform == "win32"] +[:sys_platform == "win32" and platform_python_implementation != "PyPy"] pywin32>=1.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jupyter_core-4.7.1/setup.cfg new/jupyter_core-4.8.1/setup.cfg --- old/jupyter_core-4.7.1/setup.cfg 2021-01-31 19:30:07.000000000 +0100 +++ new/jupyter_core-4.8.1/setup.cfg 2021-09-17 03:21:05.842677000 +0200 @@ -7,9 +7,10 @@ author = Jupyter Development Team author_email = jupy...@googlegroups.org url = https://jupyter.org -license = BSD +license = BSD-3-Clause license_file = COPYING.md classifiers = + Framework :: Jupyter Intended Audience :: Developers Intended Audience :: System Administrators Intended Audience :: Science/Research @@ -24,7 +25,7 @@ python_requires = >=3.6 install_requires = traitlets - pywin32>=1.0 ; sys_platform == 'win32' + pywin32>=1.0 ; sys_platform == 'win32' and platform_python_implementation != 'PyPy' [options.entry_points] console_scripts = ++++++ use_rpms_paths.patch ++++++ --- /var/tmp/diff_new_pack.1CGsPR/_old 2021-10-25 15:17:36.053681916 +0200 +++ /var/tmp/diff_new_pack.1CGsPR/_new 2021-10-25 15:17:36.053681916 +0200 @@ -9,7 +9,7 @@ --- a/jupyter_core/paths.py +++ b/jupyter_core/paths.py -@@ -117,10 +117,7 @@ +@@ -124,10 +124,7 @@ if os.name == 'nt': else: # PROGRAMDATA is not defined by default on XP. SYSTEM_JUPYTER_PATH = [os.path.join(sys.prefix, 'share', 'jupyter')] else: @@ -21,7 +21,7 @@ ENV_JUPYTER_PATH = [os.path.join(sys.prefix, 'share', 'jupyter')] -@@ -169,10 +166,7 @@ +@@ -186,10 +183,7 @@ if os.name == 'nt': else: # PROGRAMDATA is not defined by default on XP. SYSTEM_CONFIG_PATH = [] else: