From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id DFAB1138720 for ; Mon, 28 Jan 2013 23:54:21 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 83DB421C0BE; Mon, 28 Jan 2013 23:54:16 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id CDB3E21C0A7 for ; Mon, 28 Jan 2013 23:54:10 +0000 (UTC) Received: from hornbill.gentoo.org (hornbill.gentoo.org [94.100.119.163]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id AE73233DBB9 for ; Mon, 28 Jan 2013 23:54:09 +0000 (UTC) Received: from localhost.localdomain (localhost [127.0.0.1]) by hornbill.gentoo.org (Postfix) with ESMTP id 005BDE409E for ; Mon, 28 Jan 2013 23:54:06 +0000 (UTC) From: "André Erdmann" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "André Erdmann" Message-ID: <1359416224.d9996361f71ec129746861e4c312d20bfb92230a.dywi@gentoo> Subject: [gentoo-commits] proj/R_overlay:master commit in: roverlay/overlay/pkgdir/symlink/ X-VCS-Repository: proj/R_overlay X-VCS-Files: roverlay/overlay/pkgdir/symlink/__init__.py roverlay/overlay/pkgdir/symlink/distdir.py roverlay/overlay/pkgdir/symlink/distroot.py X-VCS-Directories: roverlay/overlay/pkgdir/symlink/ X-VCS-Committer: dywi X-VCS-Committer-Name: André Erdmann X-VCS-Revision: d9996361f71ec129746861e4c312d20bfb92230a X-VCS-Branch: master Date: Mon, 28 Jan 2013 23:54:06 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: ae9db64c-e15d-470f-9279-0d7e2b3f5854 X-Archives-Hash: 6ae627c91fa797b237ad2e51434fc407 commit: d9996361f71ec129746861e4c312d20bfb92230a Author: André Erdmann mailerd de> AuthorDate: Mon Jan 28 22:41:30 2013 +0000 Commit: André Erdmann mailerd de> CommitDate: Mon Jan 28 23:37:04 2013 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=d9996361 roverlay/overlay/pkgdir: SymlinkDistroot/dir A SymlinkDistroot manages zero or more per-package SymlinkDistdirs that contain symlinks to the R package files. Used for Manifest file creation. --- roverlay/overlay/pkgdir/symlink/__init__.py | 5 + roverlay/overlay/pkgdir/symlink/distdir.py | 47 ++++++ roverlay/overlay/pkgdir/symlink/distroot.py | 234 +++++++++++++++++++++++++++ 3 files changed, 286 insertions(+), 0 deletions(-) diff --git a/roverlay/overlay/pkgdir/symlink/__init__.py b/roverlay/overlay/pkgdir/symlink/__init__.py new file mode 100644 index 0000000..a7f7e7d --- /dev/null +++ b/roverlay/overlay/pkgdir/symlink/__init__.py @@ -0,0 +1,5 @@ +# R overlay -- overlay package, symlink directories +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. diff --git a/roverlay/overlay/pkgdir/symlink/distdir.py b/roverlay/overlay/pkgdir/symlink/distdir.py new file mode 100644 index 0000000..cca9b31 --- /dev/null +++ b/roverlay/overlay/pkgdir/symlink/distdir.py @@ -0,0 +1,47 @@ +# R overlay -- overlay package, symlink distdir +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. + +import os + +__all__ = [ 'SymlinkDistdir', ] + +class SymlinkDistdir ( object ): + + def __init__ ( self, root ): + """Initializes a symlink DISTDIR. + + arguments: + * root -- directory where symlinks will be created + """ + self.root = root + if not os.path.isdir ( self.root ): + os.mkdir ( self.root ) + # --- end of __init__ (...) --- + + def add ( self, fpath, fname=None ): + """Adds a symlink to a file to this dir. + + arguments: + * fpath -- path to the file for which a symlink will be created + * fname -- name of the symlink, defaults to os.path.basename ( fpath ) + """ + symlink = self.root + os.sep + ( fname or os.path.basename ( fpath ) ) + + if os.path.lexists ( symlink ): + os.unlink ( symlink ) + + if not os.path.exists ( symlink ): + os.symlink ( fpath, symlink ) + return symlink + else: + raise OSError ( "cannot set up symlink {!r}!".format ( symlink ) ) + # --- end of add (...) --- + + def __str__ ( self ): + return self.root + # --- end of __str__ (...) --- + +# --- end of SymlinkDistdir --- diff --git a/roverlay/overlay/pkgdir/symlink/distroot.py b/roverlay/overlay/pkgdir/symlink/distroot.py new file mode 100644 index 0000000..7345d98 --- /dev/null +++ b/roverlay/overlay/pkgdir/symlink/distroot.py @@ -0,0 +1,234 @@ +# R overlay -- overlay package, symlink distroot +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. + +__all__ = [ 'SymlinkDistroot', 'ThreadsafeSymlinkDistroot' ] + +import os +import atexit +import shutil +import tempfile +import threading +import logging + +import roverlay.config + +import roverlay.overlay.pkgdir.symlink.distdir + +class _SymlinkDistrootBase ( object ): + + _instance_lock = threading.Lock() + __instance = None + + @classmethod + def get_configured ( cls ): + """Returns the static SymlinkDistroot instance.""" + if cls.__instance is None: + with cls._instance_lock: + if cls.__instance is None: + cls.__instance = cls ( + root = roverlay.config.get ( + 'OVERLAY.SYMLINK_DISTROOT.root', "" + ), + is_tmpdir = roverlay.config.get_or_fail ( + 'OVERLAY.SYMLINK_DISTROOT.tmp' + ), + ) + return cls.__instance + # --- end of get_configured (...) --- + + def __repr__ ( self ): + return "{}".format ( + self.__class__.__name__, self._root, self._is_tmpdir + ) + # --- end of __repr__ (...) --- + + def __str__ ( self ): + return str ( self._root ) + # --- end of __str__ (...) --- + + def __init__ ( self, root, is_tmpdir ): + """Initializes a _SymlinkDistrootBase instance. + + arguments: + * root -- root directory where per-package SymlinkDistdirs will + be created. An empty value is only valid if is_tmpdir + evaluates to True. + * is_tmpdir -- whether this SymlinkDistroot is a temporary directory + or not. A temporary directory will be wiped at exit, + while a non-temporary one will only be cleaned up. + + tempfile.mktempd() will be used as directory if root is empty + and is_tmpdir is true. + + Raises: ConfigError if root is not set and is_tmpdir evaluates to False. + """ + super ( _SymlinkDistrootBase, self ).__init__() + + self._root = root + self._is_tmpdir = bool ( is_tmpdir ) + + if not self._root: + if self._is_tmpdir: + self._root = tempfile.mkdtemp ( prefix='tmp_roverlay_sym' ) + else: + raise roverlay.config.ConfigError ( + "OVERLAY.symlink_distroot: invalid value." + ) + elif not os.path.isdir ( self._root ): + os.makedirs ( self._root, 0o755 ) + + self._prepare() + + atexit.register ( self._destroy if self._is_tmpdir else self._cleanup ) + # --- end of __init__ (...) --- + + def _cleanup ( self ): + """Cleans up a SymlinkDistroot.""" + # derived classes can implement this to define actions for non-temporary + # SymlinkDistroots that will be performed at ext. + pass + # --- end of _cleanup (...) --- + + def _destroy ( self, force=False ): + """Destroys a SymlinkDistroot. + + arguments: + * force -- force destruction even if this distroot is not temporary + """ + if force or self._is_tmpdir: + shutil.rmtree ( self._root ) + # --- end of _destroy (...) --- + + def _get ( self, package_name ): + """Returns a new SymlinkDistdir instance for given package. + + arguments: + * package_name -- will be used as subdirectory name and must not + contain any os.sep chars (typically "/") + """ + assert os.sep not in package_name + return roverlay.overlay.pkgdir.symlink.distdir.SymlinkDistdir ( + self._root + os.sep + package_name + ) + # --- end of get (...) --- + + def _prepare ( self ): + """Prepares the SymlinkDistroot. Called for both temporary and + persistent distroots. + + The default actions is to remove broken symlinks. + """ + if not self._is_tmpdir: + self.remove_broken_symlinks ( unsafe_assumptions=True ) + # --- end of _prepare (...) --- + + def get ( self, package_name ): + """Returns a SymlinkDistdir for the given package. + + arguments: + * package_name + + Has to be implemented by derived classes. + """ + raise NotImplementedError() + # --- end of get (...) --- + + def remove_broken_symlinks ( self, unsafe_assumptions=False ): + """Recursively remove broken/dead symlinks. + + arguments: + * unsafe_assumptions -- use (potentially) faster but less safe code + """ + def recursive_remove ( root, rmdir ): + for item in os.listdir ( root ): + fpath = root + os.sep + item + + if not os.path.exists ( fpath ): + os.unlink ( fpath ) + + elif os.path.isdir ( fpath ): + recursive_remove ( fpath, True ) + if rmdir: + try: + os.rmdir ( fpath ) + except OSError: + pass + # --- end of recursive_remove (...) --- + + if unsafe_assumptions: + # completely "unroll" the recursion using the following assumptions: + # * self._root contains directories only + # * maximum recursion depth is 1 (no nested subdir structure) + + for d_item in os.listdir ( self._root ): + d_path = self._root + os.sep + d_item + + for l_item in os.listdir ( d_path ): + l_path = self._root + os.sep + l_item + if not os.path.exists ( l_path ): + os.unlink ( l_path ) + + try: + os.rmdir ( dpath ) + except OSError: + pass + else: + recursive_remove ( self._root, False ) + + # --- end of remove_broken_symlinks (...) --- + +# --- end of _SymlinkDistrootBase --- + +class SymlinkDistroot ( _SymlinkDistrootBase ): + """A symlink distroot that uses a dict to remember + per-package SymlinkDistdirs. + """ + + # _not_ threadsafe, but get() is expected to be called + # within a (per-package_name) threadsafe context + + def __init__ ( self, *args, **kwargs ): + """see _SymlinkDistrootBase.__init__()""" + super ( SymlinkDistroot, self ).__init__ ( *args, **kwargs ) + self._subdirs = dict() + # --- end of __init__ (...) --- + + def get ( self, package_name ): + """Returns a SymlinkDistdir for the given package. + + arguments: + * package_name -- + """ + try: + return self._subdirs [package_name] + except KeyError: + self._subdirs [package_name] = self._get ( package_name ) + return self._subdirs [package_name] + # --- end of get (...) --- + +# --- end of SymlinkDistroot --- + +class ThreadsafeSymlinkDistroot ( SymlinkDistroot ): + """Like SymlinkDistroot, but locks while creating SymlinkDistdirs.""" + + # will be removed if SymlinkDistroot is sufficient. + + def __init__ ( self, *args, **kwargs ): + super ( ThreadsafeSymlinkDistroot, self ).__init__ ( *args, **kwargs ) + self._lock = threading.Lock() + # --- end of __init__ (...) --- + + def get ( self, package_name ): + try: + return self._subdirs [package_name] + except KeyError: + with self._lock: + if package_name not in self._subdirs: + self._subdirs [package_name] = self._get ( package_name ) + return self._subdirs [package_name] + # --- end of get (...) --- + +# --- end of ThreadsafeSymlinkDistroot ---