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.

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)