docs: def has(selfie, self, args, kwargs):
      """This is called 'has' but is called indirectly.  Each Property sub-class
      is installed with this function which replaces their __new__.
  
      It is called 'has', because it runs during property declaration, processes
      the arguments and is responsible for returning an appropriate Property
      subclass.  As such it is identical to the 'has' function in Perl's Moose.
      The API does not use the word, but the semantics are the same.
  
      It is responsible for picking which sub-class of 'self' to invoke.
      Unlike Moose, it will not dynamically create property types; if a type
      does not exist it will be a hard error.
  
      This function should *only* be concerned with picking the appropriate
      object type, because unlike in Perl, python cannot re-bless objects from
      one class to another.
      """
      if args:
          raise exc.PositionalArgumentsProhibited()
      extra_traits = set(kwargs.pop('traits', tuple()))
  
      safe_unless_ro = self.__safe_unless_ro__ or any(
          x in kwargs for x in ("required", "isa", "check")
      )
      # detect initializer arguments only supported by a subclass and add
      # them to extra_traits
      for argname in kwargs:
          if argname not in self.all_duckwargs:
              # initializer does not support this arg.  Do any subclasses?
              implies_traits = set()
              for traits, proptype in DUCKWARGS[argname]:
                  if isinstance(proptype, type(self)):
                      implies_traits.add(traits)
                      if proptype.__safe_unless_ro__:
                          safe_unless_ro = True
              if len(implies_traits) > 1:
                  raise exc.AmbiguousPropertyTraitArg(
                      trait_arg=argname,
                      could_be=" ".join(
                          sorted(x.__name__ for x in implies_traits)
                      ),
                      matched_traits=implies_traits,
                  )
              elif not implies_traits:
                  raise exc.PropertyArgumentNotKnown(
                      badkwarg=argname,
                      badkwarg_value=kwargs[argname],
                      proptypename=self.__name__,
                      proptype=self,
                  )
              else:
                  extra_traits.update(list(implies_traits)[0])
  
      all_traits = set(self.traits) | extra_traits
  
      if "unsafe" in all_traits and "safe" not in all_traits:
          all_traits.remove("unsafe")
      elif "ro" not in all_traits and safe_unless_ro:
          all_traits.add("safe")
  
      if "v1" not in all_traits:
          if 'default' in kwargs and looks_like_v1_none(kwargs['default']):
              all_traits.add("v1")
              if 'safe' not in all_traits:
                  all_traits.add("safe")
  
      trait_set_key = tuple(sorted(all_traits))
  
      if trait_set_key not in PROPERTY_TYPES:
          create_property_type_from_traits(trait_set_key)
      property_type = PROPERTY_TYPES[trait_set_key]
      if not isinstance(property_type, type(self)):
          raise exc.PropertyTypeMismatch(
              selected=type(property_type).__name__,
              base=type(self).__name__,
          )
  
      return super(selfie, self).__new__(property_type)