Exemple #1
0
    def parse_and_execute(self, args=None):
        """Parses the given argument list and executes the command.
        @note will catch exceptions and translates them into exit codes and logging output
        @param args tuple or list of argument strings. e.g. sys.argv[1:]. If None, sys.argv[1:0] is used automatically
        @return exit code as integer between 0 and 255. If an unhandled exception occurred, 255 will be returned
        @note If we implement subcommands, giving no arguments will print usage information and set an error code
        """
        if args is None:
            args = sys.argv[1:]
        # end handle args default

        parser = self.argparser()
        try:
            if self._has_subcommands() and not args:
                parser.print_usage()
                return self.ERROR
            # print usage if nothing was specified
            parsed_args, remaining_args = parser.parse_known_args(args)
            if remaining_args:
                # traverse the subcommand chain and check if the last one actually allows unknown args
                level = self._level
                cmd = self
                unknown_allowed = False
                while cmd and not unknown_allowed:
                    cmd = getattr(parsed_args, self._subcommand_slot_name(level), None)
                    unknown_allowed |= cmd and cmd.allow_unknown_args or False
                    level += 1
                # end while there is no one to allow unknowns

                if not unknown_allowed:
                    sys.stderr.write("The following arguments could not be parsed: '%s'\n" % ' '.join(remaining_args))
                    return self.ERROR
                # end abort if no one allowed them
            # end handle remaining
            return self.execute(parsed_args, remaining_args)
        except ArgparserHandledCall as info:
            # Happens on help or version - exit with error anyway as we didn't do anything useful
            if info.message:
                sys.stdout.write(info.message + '/n')
            return self.ARGUMENT_HANDLED
        except ParserError as err:
            self.log().error(str(err))
            return self.ARGUMENT_ERROR
        except InputError as err:
            cmd = getattr(parsed_args, self._subcommand_slot_name(), None)
            (cmd and cmd.log() or self.log()).error(str(err))
            return self.ARGUMENT_ERROR
        except (ArgumentError, ArgumentTypeError) as err:
            parser.print_usage(sys.stderr)
            self.log().error(str(err))
            return self.ARGUMENT_ERROR
        except SuccessfulBreak:
            return self.SUCCESS
        except KeyboardInterrupt:
            # Signal 15, or Ctrl+C
            self.log().error("Interrupted by user")
            return self.KEYBOARD_INTERRUPT
        except Exception as err:
            self.log().error("An unhandled exception occurred", exc_info=True)
            return self.UNHANDLED_ERROR
Exemple #2
0
 def _node_attr(self, node, attr, depth, mandatory):
     value = getattr(node, attr)
     if value is None or not value:
         return
     # end handle no-value
     
     if isinstance(value, JobDate):
         value = "%s %s %s:%s" % (value.month, value.day, value.hour, value.minute)
     elif isinstance(value, list):
         value = ', '.join(str(val) for val in value)
     elif isinstance(value, Ref):
         value = value.id
     elif isinstance(node, Cmd) and attr == 'appname':
         # needs special handling, as args is a variables argument list we have parsed ourselves
         if node.args:
             value += ' ' + ' '.join(str(arg) for arg in node.args)
         # end handle node args
     else:
         value = str(value)
     # end handle different types
     
     if ' ' in value or '\n' in value:
         value = "{ %s }" % value
     # end handle brackets
     
     if not mandatory:
         self._writer('-%s ' % attr)
     # end handle arg prefix
     
     self._writer(value + ' ')
Exemple #3
0
 def _expandvars_deep(cls, path, env=os.environ):
     """As above, but recursively expands as many variables as possible"""
     rval = cls._expandvars(path, env)
     while str(rval) != str(path):
         path = rval
         rval = cls._expandvars(path)
     # END expansion loop
     return rval
Exemple #4
0
 def _insert_limits(self, max_mem_gb, max_cores, args, env, blade_data):
     """Setup prman limits using environment variables"""
     # We have a total of 2GB for the system to split up on all slots
     total_mem_relative = (blade_data.memFree - self.reserved_system_memory_gb) / blade_data.memFree
     max_mem_relative = total_mem_relative / blade_data.slotsMax
     env['RMAN_CPU_COUNT'] = str(max_cores)
     env['RMAN_MEM_LIMIT'] = str(max_mem_relative)
     
     log.log(logging.TRACE, 'rman_mem_limit = %s = %s / data.slotsMax', max_mem_relative, total_mem_relative)
Exemple #5
0
    def expand_or_raise(self):
        """@return Copy of self with all variables expanded ( using `expand` )
        non-recursively !

        :raise ValueError: If we could not expand all environment variables as
            their values where missing in the environment"""
        rval = self.expand()
        if str(rval) == str(self) and rval.containsvars():
            raise ValueError("Failed to expand all environment variables in %r, got %r" % (self, rval))
        return rval
Exemple #6
0
 def pformat(self):
     """Display the contents of the Context primarily for debugging purposes
     @return string indicating the human-readable contents
     @todo: revise printing"""
     otp = str(self)
     otp += "\t* registered instances|types:\n"
     for item in self._registry:
         otp += "\t%s\n" % item
         # Could list supported interfaces here
     otp += "\t* store:\n"
     otp += re.sub(r"(^|\n)", r"\1\t", str(self.settings())) + "\n"
     return otp
Exemple #7
0
def login_name():
    """
    Cross-platform way to get the current user login

    @attention this uses an environment variable in Windows, technically allows
    users to impersonate others quite easily.
    """
    # getuser is linux only !
    # py2: str conversion required to get unicode
    if sys.platform == 'win32':
        return str(os.environ['USERNAME'])
    else:
        return str(getpass.getuser())
Exemple #8
0
 def _insert_limits(self, max_mem_gb, max_cores, args, env, blade_data):
     """Setup limits for the maya render commands. No matter which renderer is used, we will just set up 
     all the limits we know about"""
     max_mem_MB = int(max_mem_gb * 1024)
     args[0:0] = [
         '-mr:memory', str(max_mem_MB),
         '-mr:renderThreads', str(max_cores),
         '-sw:mm', str(min(max_mem_MB, 2048)),   # yes, there is a limit of the attribute, incredible
         # The idea would be to sooner than later just use our own batch scripts for rendering, dealing
         # with settings and assets properly right away.
         #'-sw:n', str(max_cores)                 # even though this is given with -r sw -help, it doesnt work
         
         # Additionally, make sure we are verbose enough to get progress information
         '-mr:verbose', str(5),
     ]
Exemple #9
0
    def text(self, encoding=None, errors='strict'):
        r""" Open this file, read it in, return the content as a string.

        This uses "U" mode in Python 2.3 and later, so "\\r\\n" and "\\r"
        are automatically translated to '\n'.

        Optional arguments:
         * encoding - The Unicode encoding (or character set) of
           the file.  If present, the content of the file is
           decoded and returned as a unicode object; otherwise
           it is returned as an 8-bit str.
         * errors - How to handle Unicode errors; see help(str.decode)
           for the options.  Default is 'strict'.
        """
        mode = 'U'  # we are in python 2.4 at least

        f = None
        if encoding is None:
            f = self.open(mode)
        else:
            f = codecs.open(self, 'r', encoding, errors)
        # END handle encoding

        try:
            if sys.version_info[0] < 3:
                return str(f.read())
            else:
                return f.read()
            # end assure type is correct
        finally:
            f.close()
Exemple #10
0
    def _tank_instance(cls, env, paths, settings):
        """@return the initialized tank package that exists at TANK_STUDIO_INSTALL_TREE, and the context path which created
        the instance
        @param env enviornment of the to-be-started process
        @param paths from which to pull the context. They should be sorted from most specialized to to least 
        specialized
        @param settings matching the tank_engine_schema
        @throws EnvironmentError if we couldn't find it"""
        sgtk = cls._sgtk_module(env)

        if settings.force_tank_from_entity:
            if not (settings.entity_type and settings.entity_id):
                msg = "Was forced to create tank from entity, but didn't get entity information"
                raise AssertionError(msg)
            # end 

            return sgtk.tank_from_entity(settings.entity_type, settings.entity_id), 'context_path_not_set'
        else:
            errors = list()
            for path in paths:
                try:
                    return sgtk.tank_from_path(path), path
                except Exception as err:
                    errors.append(err)
            # end for each path to try
        # end handle tank instantiation mode

        raise EnvironmentError("Failed to initialize tank from any of the given context paths: %s\nErrors: %s" 
                               % (', '.join(paths), '\n'.join(str(err) for err in errors)))
Exemple #11
0
 def run(self):
     try:
         self._result = self._fun()
     except Exception as exc:
         self._exc = exc
         if self._log is not None:
             self._log.critical("%s failed" % str(self._fun), exc_info=1)
Exemple #12
0
    def test_ordered_dict_special_api(self):
        """Assert on custom API methods"""
        odict = OrderedDict()
        odict.foo = "bar"
        assert odict.foo is odict["foo"], "setattr should work, as well as getattr"

        assert isinstance(str(odict), str)
Exemple #13
0
    def test_move_fs_op(self, base_dir):
        for dry_run in range(2):
            source_item = base_dir / "directory_to_move"
            dest_item = base_dir / "move_destination"

            for creator in (source_item.mkdir, source_item.touch):
                for dest_is_dir in range(2):
                    if source_item.isdir():
                        source_item.rmdir()
                    elif source_item.isfile():
                        source_item.remove()
                    # END handle removal of existing one
                    # prep sandbox
                    if dest_item.isdir():
                        dest_item.rmdir()
                    if dest_is_dir:
                        dest_item.mkdir()
                    creator()

                    t = Transaction(log, dry_run=dry_run)
                    mo = MoveFSItemOperation(t, source_item, str(dest_item))

                    assert t.apply().succeeded()
                    assert source_item.exists() == dry_run
                    assert mo.actual_destination().exists() != dry_run
                    assert not t.rollback().succeeded()
                    assert source_item.exists()
                    assert not mo.actual_destination().exists()
Exemple #14
0
    def test_create_op(self, base_dir):
        destination = base_dir / "my_new_item"
        for dry_run in range(2):
            for content in (None, bytes(b"hello world")):
                for mode in (0o755,  None):
                    for gid in (None, os.getgid()):
                        for uid in (None, os.getuid()):
                            for dest_exists in range(2):
                                assert not destination.exists()

                                t = Transaction(log, dry_run=dry_run)
                                co = TestCreateFSItemOperation(
                                    t, str(destination), content, mode=mode, uid=uid, gid=gid)

                                if dest_exists:
                                    # Will ignore existing items, but cares about the type
                                    destination.mkdir()
                                    assert t.apply().succeeded() == (content is None)
                                    destination.rmdir()
                                else:
                                    t.apply()
                                    if not (gid or uid and os.getuid() != 0 and type(t.exception()) is OSError):
                                        assert t.succeeded()
                                        assert destination.exists() != dry_run
                                    # end ignore non-root permissions issues
                                    assert not t.rollback().succeeded()
                                    assert not destination.exists()
Exemple #15
0
 def _tree_iterator(self, context):
     """@return a python iterator which yields one nuke task with the respective command
     @note we don't set constraints, this is happening when rendering on the farm"""
     data = self._context_value(context)
     cmd = copy.deepcopy(self._cached_wrapped_command(data.job.file))
     
     # SETUP LICENSE
     # The base-class took care of selecting tags, we just check if we have to set up the 
     # interactive mode
     additional_args = list()
     if self.limit_nuke_interactive in cmd.tags:
         additional_args.append('-i')
         # make sure there is only one license - our overrides are additive, and this is a fix
         # to duplicate counts
         if self.limit_nuke_render in cmd.tags:
             cmd.tags.remove(self.limit_nuke_render)
         # end handle license
     # end handle interactive license
     
     cmd.args = cmd.args + [
         '-f',   # render full size, no proxy
         '-F %i-%i' % (data.frame.chunk.first, data.frame.chunk.last), # can do stepping as well: A-BxC
         '-V', '1', # be verbose, but not too verbose
         '-x', # execute script (rather than edit) 
         str(data.job.file)
     ] + additional_args
     
     ## allow retries in special situations, can be multiple ones
     cmd.retryrc = NukeTractorDelegate.read_error_return_code
     
     yield Task(title='nuke render', cmds = cmd)
Exemple #16
0
    def wrapper(self, *args, **kwargs):
        path = Path(_maketemp(prefix=func.__name__))
        path.mkdir()
        keep = False
        prev_val = os.environ.get('RW_DIR')
        os.environ['RW_DIR'] = str(path)
        prev_cwd = os.getcwd()
        os.chdir(path)
        try:
            try:
                return func(self, path, *args, **kwargs)
            except Exception as err:
                print(("Test %s.%s failed with error %s: '%s', output is at %r"
                       % (type(self).__name__, type(err), err, func.__name__, path)), file=sys.stderr)
                keep = True
                raise
            # end be informed about failure
        finally:
            if prev_val is not None:
                os.environ['RW_DIR'] = prev_val
            # end restore state
            os.chdir(prev_cwd)

            # Need to collect here to be sure all handles have been closed. It appears
            # a windows-only issue. In fact things should be deleted, as well as
            # memory maps closed, once objects go out of scope. For some reason
            # though this is not the case here unless we collect explicitly.
            if not keep:
                gc.collect()
                shutil.rmtree(path)
Exemple #17
0
    def _resolve_scalar_value(self, key, value):
        """@return a resolved single scalar string value"""
        # Actually, all of the values we see should be strings
        # however, the caller is and may be 'stupid', so we handle it here
        if not isinstance(value, string_types):
            return value
        # end ignore non-string types

        formatter = self.StringFormatterType()
        try:
            last_value = ''
            count = 0
            while last_value != value:
                count += 1
                last_value = value
                new_value = formatter.vformat(value, [], self._data)
                # we could have string-like types, and format degenerates them to just strings
                if type(new_value) is not type(value):
                    new_value = type(value)(new_value)
                value = new_value
                if count > MAX_ITERATIONS:
                    raise AssertionError(
                        "Value at '%s' could not be resolved after %i iterations - recursive values detected, last value was '%s', new value was '%s'" % (key, count, last_value, new_value))
                # end
            # end recursive resolution
            return value
        except (KeyError, AttributeError, ValueError, TypeError) as err:
            msg = "Failed to resolve value '%s' at key '%s' with error: %s"
            self._log.warn(msg, value, key, str(err))
            # if we can't resolve, we have to resolve substitute to an empty value. Otherwise
            # the application might continue using a format string, which it can't check for
            # validity at all. Default values (like empty strings) can though
            return type(value)()
Exemple #18
0
 def _force_removal(self, destination):
     """Forcefully delete given directory or file, linux only.
     @throws OSError"""
     self.log.info("about to remove directory at %s ... " % destination)
     rval = subprocess.call([self.rm_path, "-Rf", str(destination)])
     if rval != 0:
         raise OSError("Failed to remove file or directory that we managed to copy previously: %s" % destination)
     self.log.info("... done removing destination path")
Exemple #19
0
 def append(self, value):
     """Assured proper type and case"""
     if not isinstance(value, str):
         value = str(value)
     value = value.lower()
     if value in self:
         return
     super(Tags, self).append(value)
Exemple #20
0
 def _from_data(self, job):
     self.ui.title.setText(job.title)
     self.ui.file.setText(str(job.file))
     self.ui.comment.setText(job.comment)
     self.ui.service.setText(job.service)
     if job.comment or job.service:
         self.ui.advanced.setChecked(True)
         self.ui.advanced_widget.setVisible(True)
Exemple #21
0
 def _to_string_key(cls, key):
     """Assure the key is not mal-formed, convert None to ''"""
     if key is RootKey:
         return str()
     # end convert None to ''
     # py2/3: comparing string is difficult, as they need to be unicode
     # Here we easily get deserialized strings, which are not necessarily unicode in py2
     assert isinstance(key, (str, __builtins__["str"])) and cls.key_separator not in key
     return key
Exemple #22
0
 def _transform(cls, value):
     if cls._is_valid_member(value):
         return value
     # end handle value is valid already
     try:
         return cls.MemberType(value)
     except Exception as err:
         msg = "Conversion of '%s' (%s) to type %s failed with error: %s"
         log.error(msg, value, type(value).__name__, cls.MemberType.__name__, str(err))
         return cls.MemberType()
Exemple #23
0
 def _search_re(self):
     if self.__search_re is None:
         try:
             self.__search_re = re.compile(self._expression, self._re_flags | re.DOTALL)
         except AssertionError:
             e = sys.exc_info()[1]   # to keep py3k and backward compat
             if str(e).endswith('this version only supports 100 named groups'):
                 raise TooManyFields('sorry, you are attempting to parse too '
                                     'many complex fields')
     return self.__search_re
Exemple #24
0
 def pformat(self):
     """ print a comprehensive representation of the stack 
         @todo convert this into returning a data structure which would be useful and printable            
     """
     otp = str()
     for idx, env in enumerate(self._stack):
         otp += "### Context %i - %s ###############\n\n" % (idx, env.name())
         otp += env.pformat()
     # for each env on stack
     return otp
Exemple #25
0
 def __str__(self):
     """@return ourselves as list of element names, separated with the actual separator"""
     out = str()
     if self:
         for index in range(len(self) - 1):
             elm = self[index]
             out += elm.name() + elm.child_separator
         # end for each node
         out += self[-1].name()
     # end if we have at least an item
     return out
Exemple #26
0
    def pre_start(self, executable, env, args, cwd, resolve):
        executable, env, new_args, cwd = super(TankCommandDelegate, self).pre_start(executable, env, args, cwd, resolve)
        # and the second argument must be the tank install root ... lets make it happy
        if len(new_args) > 2 and not os.path.isabs(new_args[1]):
            install_root = Path(new_args[0]).dirname().dirname().dirname()
            assert install_root.basename() == 'install', "Expected first argument '%s' to be tank_cmd.py right in the install root" % new_args[0]
            new_args.insert(1, install_root.dirname())
        # end handle install root

        last_arg = new_args[-1]
        if not last_arg.startswith(self.tank_pc_arg):
            # we assume to be in the right spot, but a check can't hurt until
            # we are able to do more ourselves
            actual_executable = self._actual_executable()
            base = actual_executable.dirname()
            assert (base / 'tank').exists(), "Currently '%s' must be right next to the 'tank' executable" % executable
            new_args.append(str(self.tank_pc_arg + base))
        # end setup context

        #######################
        # Process Arguments ##
        #####################
        if len(new_args) > 6 and new_args[3].startswith(self.launch_prefix):
            # now we could go crazy and try to find asset paths in order to provide context to bprocess
            # We could also use the shotgun context in some way, to feed data to our own asset management
            # However, for now using the project itself should just be fine, but this is certainly 
            # to be improved

            # Additinally, what we really want is to start any supported program, and enforce tank support by
            # fixing up delegates. For that, we will create a new process controller, which uses our Application 
            # instance, and the delegate that it defined so far.
            # However, we are currently unable to truly provide the information we have to a new process controller, 
            # unless it's communicated via the context.

            # It should be one of ours (e.g. TankEngineDelegate derivative) if there is tank support, which 
            # requires proper configuration.
            
            # For that to work, we will override the entire start procedure, as in pre-start we can't and should not
            # swap in the entire delegate
            def set_overrides(schema, value):
                value.host_app_name = new_args[3][len(self.launch_prefix):]
                value.entity_type = new_args[4]
                value.entity_id = int(new_args[5])
            # end overrides setter

            # This call will also push the context onto the stack, nothing more to be done here
            self.ApplyChangeContextType('tank-engine-information').setup(self._app.context(),
                                                                         set_overrides, 
                                                                         tank_engine_schema)
        #end handle particular command mode

        return (executable, env, new_args, cwd)
Exemple #27
0
    def _assert_element_node_list(self, nlist):
        """verify functionality of element node list"""
        assert len(nlist) > 0, "nlist should not be empty"
        self.failUnless(nlist.is_leaf() == (len(nlist[-1].children()) == 0))

        assert nlist.find_first_if(lambda elm: 'Root' in elm.type()), "Every node list should have a type"
        assert not str(nlist).startswith(nlist[-1].child_separator)

        # just make the call to execute code - it can complain about a few things that subclasses may implement
        # differently
        index = dict()
        for elm in nlist:
            elm.validate(index)
Exemple #28
0
 def _sgtk_module(cls, env):
     """@return the sgtk package, as demoninated in the environment
     @throws EnvironmentError if we couldn't find it"""
     root = env.get('TANK_STUDIO_INSTALL_TREE')
     if not root:
         raise EnvironmentError("Expected TANK_STUDIO_INSTALL_TREE environment variable to be set")
     root = Path(root) / 'core' / 'python'
     sys.path.append(str(root))
     try:
         import sgtk
     except ImportError as err:
         raise EnvironmentError("Failed to import tank from '%s' with error: %s", root, str(err))
     # end
     return sgtk
Exemple #29
0
 def _match_re(self):
     if self.__match_re is None:
         expression = '^%s$' % self._expression
         try:
             self.__match_re = re.compile(expression, self._re_flags | re.DOTALL)
         except AssertionError:
             e = sys.exc_info()[1]   # to keep py3k and backward compat
             if str(e).endswith('this version only supports 100 named groups'):
                 raise TooManyFields('sorry, you are attempting to parse too '
                                     'many complex fields')
         except re.error:
             raise NotImplementedError(
                 "Group names (e.g. (?P<name>) can cause failure, as they are not esacped properly: '%s'" % expression)
     return self.__match_re
Exemple #30
0
    def apply_overrides(self, schema, args):
        """Parse overrides and set them into a new context
        @param schema KeyValueStoreSchema of your command
        @param all override values as 'key=value' string
        @note to be called in execute() method"""
        if args.overrides:
            env = self.application().context().push('user overrides')
            kvstore = env._kvstore
            for kvstring in args.overrides:
                k, v = parse_key_value_string(kvstring, self.key_value_separator)
                kvstore.set_value('%s.%s' % (schema.key(), k), v)
            # end for each string to apply
        # end handle overrides

        if getattr(args, 'show_settings', None):
            sys.stdout.write("%s.*\n" % schema.key())
            sys.stdout.write(str(self.application().context().settings().value_by_schema(schema)))
            raise SuccessfulBreak