Commits

rblank  committed 503506e

0.13dev: Allow exporting permissions to `stdout` with `permission export` and importing from `stdin` with `permission import`.

Initial patch by Peter Suter. Part of #9336.

  • Participants
  • Parent commits 65b06e7
  • Branches trunk

Comments (0)

Files changed (4)

File trac/admin/tests/console-tests.txt

 milestone remove     Remove milestone
 milestone rename     Rename milestone
 permission add       Add a new permission rule
-permission export    Export permission rules to a file
-permission import    Import permission rules from a file
+permission export    Export permission rules to a file or stdout
+permission import    Import permission rules from a file or stdin
 permission list      List permission rules
 permission remove    Remove a permission rule
 priority add         Add a priority value option

File trac/admin/tests/console.py

     def tearDown(self):
         self.env = None
 
-    def _execute(self, cmd, strip_trailing_space=True):
+    def _execute(self, cmd, strip_trailing_space=True, input=None):
+        _in = sys.stdin
         _err = sys.stderr
         _out = sys.stdout
         try:
+            if input:
+                sys.stdin = StringIO(input)
             sys.stderr = sys.stdout = out = StringIO()
             setattr(out, 'encoding', 'utf-8') # fake output encoding
             retval = None
             else:
                 return retval, value
         finally:
+            sys.stdin = _in
             sys.stderr = _err
             sys.stdout = _out
 
     def test_permission_export_ok(self):
         """
         Tests the 'permission export' command in trac-admin.  This particular
-        test exports the default permissions to a file.
+        test exports the default permissions to stdout.
         """
         test_name = sys._getframe().f_code.co_name
-        filename = 'permissions.csv'
-        rv, output = self._execute('permission export ' + filename)
+        rv, output = self._execute('permission export')
         self.assertEqual(0, rv)
-        self.assertEqual('', output)
-        filecontent = read_file(filename, 'rb')
-        os.unlink(filename)
-        self.assertEqual(self.expected_results[test_name], filecontent)
+        self.assertEqual(self.expected_results[test_name], output)
 
     def test_permission_import_ok(self):
         """
         test exports additional permissions, removes them and imports them back.
         """
         test_name = sys._getframe().f_code.co_name
-        filename = 'permissions.csv'
         self._execute('permission add test_user WIKI_VIEW')
         self._execute('permission add test_user TICKET_VIEW')
-        self._execute('permission export ' + filename)
+        rv, output = self._execute('permission export')
         self._execute('permission remove test_user *')
-        rv, output = self._execute('permission import ' + filename)
+        rv, output = self._execute('permission import', input=output)
         self.assertEqual(0, rv)
         self.assertEqual('', output)
-        os.unlink(filename)
         rv, output = self._execute('permission list')
         self.assertEqual(0, rv)
         self.assertEqual(self.expected_results[test_name], output)

File trac/perm.py

 from trac.config import ExtensionOption, OrderedExtensionsOption
 from trac.core import *
 from trac.resource import Resource, get_resource_name
+from trac.util import file_or_std
 from trac.util.text import print_table, printout, wrap
 from trac.util.translation import _
 
         yield ('permission remove', '<user> <action> [action] [...]',
                'Remove a permission rule',
                self._complete_remove, self._do_remove)
-        yield ('permission export', '<filename>',
-               'Export permission rules to a file',
+        yield ('permission export', '[file]',
+               'Export permission rules to a file or stdout',
                self._complete_import_export, self._do_export)
-        yield ('permission import', '<filename>',
-               'Import permission rules from a file',
+        yield ('permission import', '[file]',
+               'Import permission rules from a file or stdin',
                self._complete_import_export, self._do_import)
     
     def get_user_list(self):
                     _("Cannot remove permission %(action)s for user %(user)s.",
                       action=action, user=user))
     
-    def _do_export(self, filename):
+    def _do_export(self, filename=None):
         try:
-            with open(filename, 'wb') as f:
-                writer = csv.writer(f, lineterminator=os.linesep)
+            with file_or_std(filename, 'wb') as f:
+                linesep = os.linesep if filename else '\n'
+                writer = csv.writer(f, lineterminator=linesep)
                 users = self.get_user_list()
                 for user in sorted(users):
                     actions = sorted(self.get_user_perms(user))
                 _("Cannot export to %(filename)s: %(error)s",
                   filename=filename, error=e.strerror))
     
-    def _do_import(self, filename):
+    def _do_import(self, filename=None):
         permsys = PermissionSystem(self.env)
         try:
-            with open(filename, 'rb') as f:
-                reader = csv.reader(f, lineterminator=os.linesep)
+            with file_or_std(filename, 'rb') as f:
+                linesep = os.linesep if filename else '\n'
+                reader = csv.reader(f, lineterminator=linesep)
                 for row in reader:
                     if len(row) < 2:
                         raise AdminCommandError(

File trac/util/__init__.py

     return path == parent or path.startswith(parent + os.sep)
 
 
+class file_or_std(object):
+    """Context manager for opening a file or using a standard stream
+    
+    If `filename` is non-empty, open the file and close it when exiting the
+    block. Otherwise, use `sys.stdin` if opening for reading, or `sys.stdout`
+    if opening for writing or appending."""
+    
+    file = None
+    
+    def __init__(self, filename, mode='r', bufsize=-1):
+        self.filename = filename
+        self.mode = mode
+        self.bufsize = bufsize
+
+    def __enter__(self):
+        if not self.filename:
+            return sys.stdin if 'r' in self.mode else sys.stdout
+        self.file = open(self.filename, self.mode, self.bufsize)
+        return self.file
+
+    def __exit__(self, et, ev, tb):
+        if self.file is not None:
+            self.file.close()
+
+        
 # -- sys utils
 
 def arity(f):