asda?‰PNG  IHDR ? f ??C1 sRGB ??é gAMA ±? üa pHYs ? ??o¨d GIDATx^íüL”÷e÷Y?a?("Bh?_ò???¢§?q5k?*:t0A-o??¥]VkJ¢M??f?±8\k2íll£1]q?ù???T PKHe[I3files.pynu[# -*- coding: utf-8 -*- # slip.util.files -- file helper functions # # Copyright © 2009, 2010, 2012, 2015 Red Hat, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Authors: # Nils Philippsen """This module contains helper functions for dealing with files.""" from __future__ import absolute_import # ensure range() returns a generator if 'xrange' in dir(__builtins__): range = xrange __all__ = ["issamefile", "linkfile", "copyfile", "linkorcopyfile", "overwrite_safely"] import os import selinux import tempfile import errno import stat BLOCKSIZE = 1024 def _issamefile(path1, path2): s1 = os.stat(path1) s2 = os.stat(path2) return os.path.samestat(s1, s2) def issamefile(path1, path2, catch_stat_exceptions=[]): """Check whether two paths point to the same file (i.e. are hardlinked).""" if catch_stat_exceptions is True: catch_stat_exceptions = Exception try: return _issamefile(path1, path2) except catch_stat_exceptions: return False def linkfile(srcpath, dstpath): """Hardlink srcpath to dstpath. Attempt to atomically replace dstpath if it exists.""" if issamefile(srcpath, dstpath, catch_stat_exceptions=OSError): return dstpath = os.path.abspath(dstpath) dstdname = os.path.dirname(dstpath) dstbname = os.path.basename(dstpath) hardlinked = False for attempt in range(tempfile.TMP_MAX): _dsttmp = tempfile.mktemp(prefix=dstbname + os.extsep, dir=dstdname) try: os.link(srcpath, _dsttmp) except OSError as e: if e.errno == errno.EEXIST: # try another name pass else: raise else: hardlinked = True break if hardlinked: os.rename(_dsttmp, dstpath) def copyfile(srcpath, dstpath, copy_mode_from_dst=True, run_restorecon=True): """Copy srcpath to dstpath. Abort operation if e.g. not enough space is available. Attempt to atomically replace dstpath if it exists.""" if issamefile(srcpath, dstpath, catch_stat_exceptions=OSError): return dstpath = os.path.abspath(dstpath) dstdname = os.path.dirname(dstpath) dstbname = os.path.basename(dstpath) srcfile = open(srcpath, "rb") dsttmpfile = tempfile.NamedTemporaryFile( prefix=dstbname + os.path.extsep, dir=dstdname, delete=False) s = os.stat(srcpath) if copy_mode_from_dst: # attempt to copy mode from destination file (if it exists, # otherwise fall back to copying it from the source file below) try: s = os.stat(dstpath) except OSError: pass os.fchmod(dsttmpfile.fileno(), stat.S_IMODE(s.st_mode)) data = None while data != "": data = srcfile.read(BLOCKSIZE) try: dsttmpfile.write(data) except: srcfile.close() dsttmpfile.close() os.unlink(dsttmpfile.name) raise srcfile.close() dsttmpfile.close() os.rename(dsttmpfile.name, dstpath) if run_restorecon and selinux.is_selinux_enabled() > 0: selinux.restorecon(dstpath) def linkorcopyfile( srcpath, dstpath, copy_mode_from_dst=True, run_restorecon=True): """First attempt to hardlink srcpath to dstpath, if hardlinking isn't possible, attempt copying srcpath to dstpath.""" try: linkfile(srcpath, dstpath) return except OSError as e: if e.errno not in (errno.EMLINK, errno.EPERM, errno.EXDEV): # don't bother copying raise else: # try copying pass copyfile(srcpath, dstpath, copy_mode_from_dst, run_restorecon) def symlink_atomically(srcpath, dstpath, force=False, preserve_context=True): """Create a symlink, optionally replacing dstpath atomically, optionally setting or preserving SELinux context.""" dstdname = os.path.dirname(dstpath) dstbname = os.path.basename(dstpath) run_restorecon = False ctx = None if preserve_context and selinux.is_selinux_enabled() <= 0: preserve_context = False else: try: ret, ctx = selinux.lgetfilecon(dstpath) if ret < 0: raise RuntimeError("getfilecon(%r) failed" % dstpath) except OSError as e: if e.errno == errno.ENOENT: run_restorecon = True else: raise if not force: os.symlink(srcpath, dstpath) if preserve_context: selinux.restorecon(dstpath) else: dsttmp = None for attempt in range(tempfile.TMP_MAX): _dsttmp = tempfile.mktemp( prefix=dstbname + os.extsep, dir=dstdname) try: os.symlink(srcpath, _dsttmp) except OSError as e: if e.errno == errno.EEXIST: # try again continue raise else: dsttmp = _dsttmp break if dsttmp is None: raise IOError( errno.EEXIST, "No suitable temporary symlink could be created.") if preserve_context and not run_restorecon: selinux.lsetfilecon(dsttmp, ctx) try: os.rename(dsttmp, dstpath) except: # clean up os.remove(dsttmp) raise if run_restorecon: selinux.restorecon(dstpath) def overwrite_safely( path, content, preserve_mode=True, preserve_context=True, preserve_ownership=True): """Safely overwrite a file by creating a temporary file in the same directory, writing it, moving it over the original file, eventually preserving file mode, SELinux context and ownership.""" path = os.path.realpath(path) dir_ = os.path.dirname(path) base = os.path.basename(path) fd = None f = None tmpname = None exists = os.path.exists(path) if preserve_context and selinux.is_selinux_enabled() <= 0: preserve_context = False try: fd, tmpname = tempfile.mkstemp(prefix=base + os.path.extsep, dir=dir_) if exists: s = os.stat(path) if preserve_ownership: os.fchown(fd, s.st_uid, s.st_gid) if preserve_mode: os.fchmod(fd, stat.S_IMODE(s.st_mode)) if preserve_context: ret, ctx = selinux.getfilecon(path) if ret < 0: raise RuntimeError("getfilecon(%r) failed" % path) f = os.fdopen(fd, "w") fd = None f.write(content) f.close() f = None os.rename(tmpname, path) if preserve_context: if exists: selinux.setfilecon(path, ctx) else: selinux.restorecon(path) finally: if f: f.close() elif fd: os.close(fd) if tmpname and os.path.isfile(tmpname): try: os.unlink(tmpname) except: pass PKHe[ɫ#__pycache__/__init__.cpython-36.pycnu[3 uAcl@s(ddlmZddlmZddlmZdS))absolute_import)hookable)filesN)Z __future__rrrrr/usr/lib/python3.6/__init__.pys  PKHe[ɫ)__pycache__/__init__.cpython-36.opt-1.pycnu[3 uAcl@s(ddlmZddlmZddlmZdS))absolute_import)hookable)filesN)Z __future__rrrrr/usr/lib/python3.6/__init__.pys  PKHe[og0)__pycache__/hookable.cpython-36.opt-1.pycnu[3 uAc^@sldZddlZddlmZddgZGdddeZGdd d eZGd ddeeeZ Gd dde e Z dS) z[This module contains variants of certain base types which call registered hooks on changes.N)with_metaclassHookable HookableSetc@s eZdZddZeddZdS) HookableTypec Csd|kry |d}WnJtk r^d}x0dd|DD]}|rRtdt|q8|}q8WYnXx |dD]}tj||||<qjWtj||||S)N_hookable_change_methodsZ_hookable_base_classcss|]}|tkr|VqdS)N)r).0xr /usr/lib/python3.6/hookable.py )sz'HookableType.__new__..ztoo many base classes: %s)KeyError TypeErrorstrr wrap_methodtype__new__)clsnamebasesZdctbaseZbase_candidate methodnamer r r r"s zHookableType.__new__cs t||fdd}||_|S)Ncs|f||}|j|S)N) _run_hooks)selfpkZretval)funcr r methodwrapper9sz/HookableType.wrap_method..methodwrapper)getattr__name__)rrrrr )rr r5s  zHookableType.wrap_methodN)r __module__ __qualname__r classmethodrr r r r r src@s6eZdZd ddZddZddZdd Zd d ZdS) _HookEntryNc CsxFt|D]:\}}y t|Wq tk rBtd||fYq Xq WxF|jD]:\}}y t|WqRtk rtd||fYqRXqRWt|tst|}||_||_||_||_ d|_ dS)Nz*Positional argument %d is not hashable: %rz'Keyword argument %r is not hashable: %r) enumeratehashr items isinstancetuple_HookEntry__hook_HookEntry__args_HookEntry__kwargs_HookEntry__hookable_HookEntry__hash)rhookargskwargshookablenrrr r r __init__Ds*   z_HookEntry.__init__cCs$|j|jko"|j|jko"|j|jkS)N)r(r)r*)robjr r r __cmp__cs  z_HookEntry.__cmp__cCs|js|j|_|jS)N)r, _compute_hash)rr r r __hash__is z_HookEntry.__hash__cCs>t|j}t|t|jA}t|ttt|jjA}|S)N)r$r(r)r'sortedr*r%)r hashvaluer r r r5ns  z_HookEntry._compute_hashcCs4|jr |j|jf|j|jn|j|j|jdS)N)r+r(r)r*)rr r r runusz_HookEntry.run)N)rrr r2r4r6r5r9r r r r r"Bs  r"c@seZdZdZeddZddZddZeeeZdd Z d d Z ee e Z d d Z ddZ ddZddZddZddZddZdS)rz2An object which calls registered hooks on changes.cOst|dst|_|jS)N__real_hooks__)hasattrsetr:)rrrr r r __hooks__s zHookable.__hooks__cCst|dsd|_|jS)N__hooks_enabled__T)r;r>)rr r r _get_hooks_enableds zHookable._get_hooks_enabledcCs ||_dS)N)r>)rZenabledr r r _set_hooks_enabledszHookable._set_hooks_enabledcCst|dsd|_|jS)N__hooks_frozen__F)r;rA)rr r r _get_hooks_frozens zHookable._get_hooks_frozencCsB||jkrdS||_|r"t|_nx|jD] }|jq*W|`dS)N) hooks_frozenrAr<__hooks_frozen_entries__r9)rZfreeze hookentryr r r _set_hooks_frozens    zHookable._set_hooks_frozencCs d|_dS)NT)rC)rr r r freeze_hooksszHookable.freeze_hookscCs d|_dS)NF)rC)rr r r thaw_hooksszHookable.thaw_hookscOs|j|df||dS)N)_Hookable__add_hook)rr-r.r/r r r add_hookszHookable.add_hookcOs|j||f||dS)N)rI)rr-r.r/r r r add_hook_hookableszHookable.add_hook_hookablecOs t||||d}|jj|dS)N)r0)r"r=add)rr-Z _hookabler.r/rEr r r Z __add_hookszHookable.__add_hookcOs|jjt|||dS)N)r=remover")rr-r.r/r r r remove_hookszHookable.remove_hookcCs8|jr4|js&x&|jD] }|jqWn|jj|jdS)N) hooks_enabledrCr=r9rDupdate)rrEr r r rs  zHookable._run_hooksN)rrr __doc__propertyr=r?r@rOrBrFrCrGrHrJrKrIrNrr r r r r|s   c @seZdZdZdZd d Zd S)rz5A set object which calls registered hooks on changes.rLcleardifference_updatediscardintersection_updatepoprMsymmetric_difference_updaterPcCstj|}t|_|S)N)r<copyr:)rr3r r r rYs zHookableSet.copyN) rLrSrTrUrVrWrMrXrP)rrr rQrrYr r r r rs) rQ collectionsZsixr__all__rrobjectr"rr<rr r r r s ":GPKHe[[ UU&__pycache__/files.cpython-36.opt-1.pycnu[3 uAc@sdZddlmZdeekr eZdddddgZdd lZdd l Z dd l Z dd l Z dd l Z d Z d d Zgfd dZddZdddZdddZdddZdddZd S)z=This module contains helper functions for dealing with files.)absolute_importxrange issamefilelinkfilecopyfilelinkorcopyfileoverwrite_safelyNicCs"tj|}tj|}tjj||S)N)osstatpathsamestat)path1path2s1s2r/usr/lib/python3.6/files.py _issamefile+s  rc Cs0|dkr t}y t||S|k r*dSXdS)zECheck whether two paths point to the same file (i.e. are hardlinked).TFN) Exceptionr)r rcatch_stat_exceptionsrrrr2s  cCst||tdrdStjj|}tjj|}tjj|}d}xpttj D]b}tj |tj |d}ytj ||Wn2tk r}z|j t jkrnWYdd}~XqFXd}PqFW|rtj||dS)zUHardlink srcpath to dstpath. Attempt to atomically replace dstpath if it exists.)rNF)prefixdirT)rOSErrorr r abspathdirnamebasenamerangetempfileTMP_MAXmktempextseplinkerrnoEEXISTrename)srcpathdstpathdstdnamedstbnameZ hardlinkedattempt_dsttmperrrr>s$    Tc Cs8t||tdrdStjj|}tjj|}tjj|}t|d}tj |tjj |dd}tj |}|rytj |}Wntk rYnXtj |j t j|jd} xP| dkr|jt} y|j| Wq|j|jtj|jYqXqW|j|jtj|j||r4tjdkr4tj|dS)zCopy srcpath to dstpath. Abort operation if e.g. not enough space is available. Attempt to atomically replace dstpath if it exists.)rNrbF)rrdeleter)rrr r rrropenrZNamedTemporaryFiler r fchmodfilenoS_IMODEst_moderead BLOCKSIZEwritecloseunlinknamer$selinuxis_selinux_enabled restorecon) r%r&copy_mode_from_dstrun_restoreconr'r(ZsrcfileZ dsttmpfilesdatarrrr_s<         cCs^yt||dStk rJ}z |jtjtjtjfkr:nWYdd}~XnXt||||dS)ztFirst attempt to hardlink srcpath to dstpath, if hardlinking isn't possible, attempt copying srcpath to dstpath.N)rrr"ZEMLINKZEPERMZEXDEVr)r%r&r=r>r+rrrrs Fc Cstjj|}tjj|}d}d}|r6tjdkr6d}n^y&tj|\}}|dkrZtd|Wn6tk r} z| j t j krd}nWYdd} ~ XnX|stj |||rtj |nd} xtt tjD]f} tj|tj|d} ytj || Wn6tk r"} z| j t jkrwĂWYdd} ~ XqX| } PqW| dkrDtt jd|r^| r^tj| |ytj| |Wntj| YnX|rtj |dS)zpCreate a symlink, optionally replacing dstpath atomically, optionally setting or preserving SELinux context.FNrzgetfilecon(%r) failedT)rrz/No suitable temporary symlink could be created.)r r rrr:r;Z lgetfilecon RuntimeErrorrr"ENOENTsymlinkr<rrrrr r#IOErrorZ lsetfileconr$remove) r%r&forcepreserve_contextr'r(r>ctxretr+Zdsttmpr)r*rrrsymlink_atomicallysV        rJcCs~tjj|}tjj|}tjj|}d}d}d} tjj|} |rPtjdkrPd}ztj |tjj |d\}} | rtj |} |rtj || j | j|rtj|t j| j|rtj|\} } | dkrtd|tj|d}d}|j||jd}tj| ||r$| rtj|| n tj|Wd|r8|jn|rHtj|| rxtjj| rxytj| Wn YnXXdS)zSafely overwrite a file by creating a temporary file in the same directory, writing it, moving it over the original file, eventually preserving file mode, SELinux context and ownership.NrF)rrzgetfilecon(%r) failedw)r r realpathrrexistsr:r;rZmkstempr r fchownst_uidst_gidr0r2r3Z getfileconrAfdopenr6r7r$Z setfileconr<isfiler8)r ZcontentZ preserve_moderGZpreserve_ownershipZdir_basefdfZtmpnamerMr?rIrHrrrrsR            )TT)TT)FT)TTT)__doc__Z __future__rr __builtins__rr__all__r r:rr"r r5rrrrrrJrrrrrs&   ! 5  ?PKHe[[ UU __pycache__/files.cpython-36.pycnu[3 uAc@sdZddlmZdeekr eZdddddgZdd lZdd l Z dd l Z dd l Z dd l Z d Z d d Zgfd dZddZdddZdddZdddZdddZd S)z=This module contains helper functions for dealing with files.)absolute_importxrange issamefilelinkfilecopyfilelinkorcopyfileoverwrite_safelyNicCs"tj|}tj|}tjj||S)N)osstatpathsamestat)path1path2s1s2r/usr/lib/python3.6/files.py _issamefile+s  rc Cs0|dkr t}y t||S|k r*dSXdS)zECheck whether two paths point to the same file (i.e. are hardlinked).TFN) Exceptionr)r rcatch_stat_exceptionsrrrr2s  cCst||tdrdStjj|}tjj|}tjj|}d}xpttj D]b}tj |tj |d}ytj ||Wn2tk r}z|j t jkrnWYdd}~XqFXd}PqFW|rtj||dS)zUHardlink srcpath to dstpath. Attempt to atomically replace dstpath if it exists.)rNF)prefixdirT)rOSErrorr r abspathdirnamebasenamerangetempfileTMP_MAXmktempextseplinkerrnoEEXISTrename)srcpathdstpathdstdnamedstbnameZ hardlinkedattempt_dsttmperrrr>s$    Tc Cs8t||tdrdStjj|}tjj|}tjj|}t|d}tj |tjj |dd}tj |}|rytj |}Wntk rYnXtj |j t j|jd} xP| dkr|jt} y|j| Wq|j|jtj|jYqXqW|j|jtj|j||r4tjdkr4tj|dS)zCopy srcpath to dstpath. Abort operation if e.g. not enough space is available. Attempt to atomically replace dstpath if it exists.)rNrbF)rrdeleter)rrr r rrropenrZNamedTemporaryFiler r fchmodfilenoS_IMODEst_moderead BLOCKSIZEwritecloseunlinknamer$selinuxis_selinux_enabled restorecon) r%r&copy_mode_from_dstrun_restoreconr'r(ZsrcfileZ dsttmpfilesdatarrrr_s<         cCs^yt||dStk rJ}z |jtjtjtjfkr:nWYdd}~XnXt||||dS)ztFirst attempt to hardlink srcpath to dstpath, if hardlinking isn't possible, attempt copying srcpath to dstpath.N)rrr"ZEMLINKZEPERMZEXDEVr)r%r&r=r>r+rrrrs Fc Cstjj|}tjj|}d}d}|r6tjdkr6d}n^y&tj|\}}|dkrZtd|Wn6tk r} z| j t j krd}nWYdd} ~ XnX|stj |||rtj |nd} xtt tjD]f} tj|tj|d} ytj || Wn6tk r"} z| j t jkrwĂWYdd} ~ XqX| } PqW| dkrDtt jd|r^| r^tj| |ytj| |Wntj| YnX|rtj |dS)zpCreate a symlink, optionally replacing dstpath atomically, optionally setting or preserving SELinux context.FNrzgetfilecon(%r) failedT)rrz/No suitable temporary symlink could be created.)r r rrr:r;Z lgetfilecon RuntimeErrorrr"ENOENTsymlinkr<rrrrr r#IOErrorZ lsetfileconr$remove) r%r&forcepreserve_contextr'r(r>ctxretr+Zdsttmpr)r*rrrsymlink_atomicallysV        rJcCs~tjj|}tjj|}tjj|}d}d}d} tjj|} |rPtjdkrPd}ztj |tjj |d\}} | rtj |} |rtj || j | j|rtj|t j| j|rtj|\} } | dkrtd|tj|d}d}|j||jd}tj| ||r$| rtj|| n tj|Wd|r8|jn|rHtj|| rxtjj| rxytj| Wn YnXXdS)zSafely overwrite a file by creating a temporary file in the same directory, writing it, moving it over the original file, eventually preserving file mode, SELinux context and ownership.NrF)rrzgetfilecon(%r) failedw)r r realpathrrexistsr:r;rZmkstempr r fchownst_uidst_gidr0r2r3Z getfileconrAfdopenr6r7r$Z setfileconr<isfiler8)r ZcontentZ preserve_moderGZpreserve_ownershipZdir_basefdfZtmpnamerMr?rIrHrrrrsR            )TT)TT)FT)TTT)__doc__Z __future__rr __builtins__rr__all__r r:rr"r r5rrrrrrJrrrrrs&   ! 5  ?PKHe[Rg#__pycache__/hookable.cpython-36.pycnu[3 uAc^@sldZddlZddlmZddgZGdddeZGdd d eZGd ddeeeZ Gd dde e Z dS) z[This module contains variants of certain base types which call registered hooks on changes.N)with_metaclassHookable HookableSetc@s eZdZddZeddZdS) HookableTypec Csd|kry |d}WnJtk r^d}x0dd|DD]}|rRtdt|q8|}q8WYnXx |dD]}tj||||<qjWtj||||S)N_hookable_change_methodsZ_hookable_base_classcss|]}|tkr|VqdS)N)r).0xr /usr/lib/python3.6/hookable.py )sz'HookableType.__new__..ztoo many base classes: %s)KeyError TypeErrorstrr wrap_methodtype__new__)clsnamebasesZdctbaseZbase_candidate methodnamer r r r"s zHookableType.__new__cs t||fdd}||_|S)Ncs|f||}|j|S)N) _run_hooks)selfpkZretval)funcr r methodwrapper9sz/HookableType.wrap_method..methodwrapper)getattr__name__)rrrrr )rr r5s  zHookableType.wrap_methodN)r __module__ __qualname__r classmethodrr r r r r src@s6eZdZd ddZddZddZdd Zd d ZdS) _HookEntryNc Cst|tjstt|tstxFt|D]:\}}y t|Wq(tk r`td||fYq(Xq(WxF|jD]:\}}y t|Wqptk rtd||fYqpXqpWt|t st |}||_ ||_ ||_ ||_ d|_dS)Nz*Positional argument %d is not hashable: %rz'Keyword argument %r is not hashable: %r) isinstance collectionsCallableAssertionErrorr enumeratehashr itemstuple_HookEntry__hook_HookEntry__args_HookEntry__kwargs_HookEntry__hookable_HookEntry__hash)rhookargskwargshookablenrrr r r __init__Ds.   z_HookEntry.__init__cCs$|j|jko"|j|jko"|j|jkS)N)r+r,r-)robjr r r __cmp__cs  z_HookEntry.__cmp__cCs|js|j|_|jS)N)r/ _compute_hash)rr r r __hash__is z_HookEntry.__hash__cCs>t|j}t|t|jA}t|ttt|jjA}|S)N)r(r+r,r*sortedr-r))r hashvaluer r r r8ns  z_HookEntry._compute_hashcCs4|jr |j|jf|j|jn|j|j|jdS)N)r.r+r,r-)rr r r runusz_HookEntry.run)N)rrr r5r7r9r8r<r r r r r"Bs  r"c@seZdZdZeddZddZddZeeeZdd Z d d Z ee e Z d d Z ddZ ddZddZddZddZddZdS)rz2An object which calls registered hooks on changes.cOst|dst|_|jS)N__real_hooks__)hasattrsetr=)rrrr r r __hooks__s zHookable.__hooks__cCst|dsd|_|jS)N__hooks_enabled__T)r>rA)rr r r _get_hooks_enableds zHookable._get_hooks_enabledcCs ||_dS)N)rA)rZenabledr r r _set_hooks_enabledszHookable._set_hooks_enabledcCst|dsd|_|jS)N__hooks_frozen__F)r>rD)rr r r _get_hooks_frozens zHookable._get_hooks_frozencCsB||jkrdS||_|r"t|_nx|jD] }|jq*W|`dS)N) hooks_frozenrDr?__hooks_frozen_entries__r<)rZfreeze hookentryr r r _set_hooks_frozens    zHookable._set_hooks_frozencCs d|_dS)NT)rF)rr r r freeze_hooksszHookable.freeze_hookscCs d|_dS)NF)rF)rr r r thaw_hooksszHookable.thaw_hookscOs|j|df||dS)N)_Hookable__add_hook)rr0r1r2r r r add_hookszHookable.add_hookcOs|j||f||dS)N)rL)rr0r1r2r r r add_hook_hookableszHookable.add_hook_hookablecOs>t|tjstt|tstt||||d}|jj|dS)N)r3)r#r$r%r&rr"r@add)rr0Z _hookabler1r2rHr r r Z __add_hookszHookable.__add_hookcOs|jjt|||dS)N)r@remover")rr0r1r2r r r remove_hookszHookable.remove_hookcCs8|jr4|js&x&|jD] }|jqWn|jj|jdS)N) hooks_enabledrFr@r<rGupdate)rrHr r r rs  zHookable._run_hooksN)rrr __doc__propertyr@rBrCrRrErIrFrJrKrMrNrLrQrr r r r r|s   c @seZdZdZdZd d Zd S)rz5A set object which calls registered hooks on changes.rOcleardifference_updatediscardintersection_updatepoprPsymmetric_difference_updaterScCstj|}t|_|S)N)r?copyr=)rr6r r r r\s zHookableSet.copyN) rOrVrWrXrYrZrPr[rS)rrr rTrr\r r r r rs) rTr$Zsixr__all__rrobjectr"rr?rr r r r s ":GPKHe[ECyH^^ hookable.pynu[# -*- coding: utf-8 -*- # slip.util.hookable -- run hooks on changes in objects # # Copyright © 2008 Red Hat, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Authors: # Nils Philippsen """This module contains variants of certain base types which call registered hooks on changes.""" import collections from six import with_metaclass __all__ = ["Hookable", "HookableSet"] class HookableType(type): def __new__(cls, name, bases, dct): if '_hookable_change_methods' in dct: try: base = dct["_hookable_base_class"] except KeyError: base = None for base_candidate in (x for x in bases if x != Hookable): if base: raise TypeError( "too many base classes: %s" % str(bases)) else: base = base_candidate for methodname in dct["_hookable_change_methods"]: dct[methodname] = HookableType.wrap_method(base, methodname) return type.__new__(cls, name, bases, dct) @classmethod def wrap_method(cls, base, methodname): func = getattr(base, methodname) def methodwrapper(self, *p, **k): retval = func(self, *p, **k) self._run_hooks() return retval methodwrapper.__name__ = methodname return methodwrapper class _HookEntry(object): def __init__(self, hook, args, kwargs, hookable=None): assert(isinstance(hook, collections.Callable)) assert(isinstance(hookable, Hookable)) for n, x in enumerate(args): try: hash(x) except TypeError: raise TypeError( "Positional argument %d is not hashable: %r" % (n, x)) for k, x in kwargs.items(): try: hash(x) except TypeError: raise TypeError( "Keyword argument %r is not hashable: %r" % (k, x)) if not isinstance(args, tuple): args = tuple(args) self.__hook = hook self.__args = args self.__kwargs = kwargs self.__hookable = hookable self.__hash = None def __cmp__(self, obj): return ( self.__hook == obj.__hook and self.__args == obj.__args and self.__kwargs == obj.__kwargs) def __hash__(self): if not self.__hash: self.__hash = self._compute_hash() return self.__hash def _compute_hash(self): hashvalue = hash(self.__hook) hashvalue = hash(hashvalue) ^ hash(self.__args) hashvalue = hash(hashvalue) ^ hash( tuple(sorted(self.__kwargs.items()))) return hashvalue def run(self): if self.__hookable: self.__hook(self.__hookable, *self.__args, **self.__kwargs) else: self.__hook(*self.__args, **self.__kwargs) class Hookable(with_metaclass(HookableType, object)): """An object which calls registered hooks on changes.""" @property def __hooks__(self, *p, **k): if not hasattr(self, "__real_hooks__"): self.__real_hooks__ = set() return self.__real_hooks__ def _get_hooks_enabled(self): if not hasattr(self, "__hooks_enabled__"): self.__hooks_enabled__ = True return self.__hooks_enabled__ def _set_hooks_enabled(self, enabled): self.__hooks_enabled__ = enabled hooks_enabled = property(_get_hooks_enabled, _set_hooks_enabled) def _get_hooks_frozen(self): if not hasattr(self, "__hooks_frozen__"): self.__hooks_frozen__ = False return self.__hooks_frozen__ def _set_hooks_frozen(self, freeze): if freeze == self.hooks_frozen: return self.__hooks_frozen__ = freeze if freeze: self.__hooks_frozen_entries__ = set() else: for hookentry in self.__hooks_frozen_entries__: hookentry.run() del self.__hooks_frozen_entries__ hooks_frozen = property(_get_hooks_frozen, _set_hooks_frozen) def freeze_hooks(self): self.hooks_frozen = True def thaw_hooks(self): self.hooks_frozen = False def add_hook(self, hook, *args, **kwargs): self.__add_hook(hook, None, *args, **kwargs) def add_hook_hookable(self, hook, *args, **kwargs): self.__add_hook(hook, self, *args, **kwargs) def __add_hook(self, hook, _hookable, *args, **kwargs): assert isinstance(hook, collections.Callable) assert isinstance(_hookable, Hookable) hookentry = _HookEntry(hook, args, kwargs, hookable=_hookable) self.__hooks__.add(hookentry) def remove_hook(self, hook, *args, **kwargs): self.__hooks__.remove(_HookEntry(hook, args, kwargs)) def _run_hooks(self): if self.hooks_enabled: if not self.hooks_frozen: for hookentry in self.__hooks__: hookentry.run() else: self.__hooks_frozen_entries__.update(self.__hooks__) class HookableSet(set, Hookable): """A set object which calls registered hooks on changes.""" _hookable_change_methods = ( "add", "clear", "difference_update", "discard", "intersection_update", "pop", "remove", "symmetric_difference_update", "update") def copy(self): obj = set.copy(self) obj.__real_hooks__ = set() return obj PKHe[`JwQll __init__.pynu[# -*- coding: utf-8 -*- from __future__ import absolute_import from . import hookable from . import files PKHe[I3files.pynu[PKHe[ɫ#__pycache__/__init__.cpython-36.pycnu[PKHe[ɫ) __pycache__/__init__.cpython-36.opt-1.pycnu[PKHe[og0)]!__pycache__/hookable.cpython-36.opt-1.pycnu[PKHe[[ UU&8__pycache__/files.cpython-36.opt-1.pycnu[PKHe[[ UU _L__pycache__/files.cpython-36.pycnu[PKHe[Rg#`__pycache__/hookable.cpython-36.pycnu[PKHe[ECyH^^ whookable.pynu[PKHe[`JwQll o__init__.pynu[PK <