code: def copy_sqla_object(obj: object,
                       omit_fk: bool = True,
                       omit_pk: bool = True,
                       omit_attrs: List[str] = None,
                       debug: bool = False) -> object:
      
      omit_attrs = omit_attrs or []  # type: List[str]
      cls = type(obj)
      mapper = class_mapper(cls)
      newobj = cls()  # not: cls.__new__(cls)
      rel_keys = set([c.key for c in mapper.relationships])
      prohibited = rel_keys
      if omit_pk:
          pk_keys = set([c.key for c in mapper.primary_key])
          prohibited |= pk_keys
      if omit_fk:
          fk_keys = set([c.key for c in mapper.columns if c.foreign_keys])
          prohibited |= fk_keys
      prohibited |= set(omit_attrs)
      if debug:
          log.debug("copy_sqla_object: skipping: {}", prohibited)
      for k in [p.key for p in mapper.iterate_properties
                if p.key not in prohibited]:
          try:
              value = getattr(obj, k)
              if debug:
                  log.debug("copy_sqla_object: processing attribute {} = {}",
                            k, value)
              setattr(newobj, k, value)
          except AttributeError:
              if debug:
                  log.debug("copy_sqla_object: failed attribute {}", k)
              pass
      return newobj
docs: Given an SQLAlchemy object, creates a new object (FOR WHICH THE OBJECT
      MUST SUPPORT CREATION USING ``__init__()`` WITH NO PARAMETERS), and copies
      across all attributes, omitting PKs (by default), FKs (by default), and
      relationship attributes (always omitted).
  
      Args:
          obj: the object to copy
          omit_fk: omit foreign keys (FKs)?
          omit_pk: omit primary keys (PKs)?
          omit_attrs: attributes (by name) not to copy
          debug: be verbose
          
      Returns:
          a new copy of the object