rpw.utils¶
Coerce / Type Casting¶
Type Casting Utilities
-
rpw.utils.coerce.
to_category
(category_reference, fuzzy=True)¶ Coerces a category, category name or category id to a BuiltInCategory.
>>> from rpw.utils.coerce import to_category >>> to_category('OST_Walls') BuiltInCategory.OST_Walls >>> to_category('Walls') BuiltInCategory.OST_Walls >>> to_category(BuiltInCategory.OST_Walls) BuiltInCategory.OST_Walls
Parameters: cateagory_reference ([ DB.BuiltInCategory
,str
,CategoryId
]) – Category Reference or NameReturns: BuiltInCategory Return type: [ BuiltInCategory
]
-
rpw.utils.coerce.
to_category_id
(category_reference, fuzzy=True)¶ Coerces a category, category name or category id to a Category Id.
>>> from rpw.utils.coerce import to_category_id >>> to_category_id('OST_Walls') <ElementId> >>> to_category_id('Wall') <ElementId> >>> to_category_id(BuiltInCategory.OST_Walls) <ElementId>
Parameters: cateagory_reference ([ DB.BuiltInCategory
,str
,CategoryId
]) – Category Reference or NameReturns: ElementId of Category Return type: [ DB.ElementId
]
-
rpw.utils.coerce.
to_class
(class_reference)¶ Coerces a class or class reference to a Class.
>>> from rpw.utils.coerce import to_class >>> to_class('Wall') [ DB.Wall ] >>> to_class(Wall) [ DB.Wall ]
Parameters: class_reference ([ DB.Wall
,str
]) – Class Reference or class nameReturns: Class Return type: [ type
]
-
rpw.utils.coerce.
to_element
(element_reference, doc=Document)¶ Same as to_elements but for a single object
-
rpw.utils.coerce.
to_element_id
(element_reference)¶ Coerces Element References (Element, ElementId, …) to Element Id
>>> from rpw.utils.coerce import to_element_id >>> to_element_id(SomeElement) <Element Id>
-
rpw.utils.coerce.
to_element_ids
(element_references)¶ Coerces an element or list of elements into element ids. Elements remain unchanged. This will always return a list, even if only one element is passed.
>>> from rpw.utils.coerce import to_element_ids >>> to_element_ids(DB.Element) [ DB.ElementId ] >>> to_element_ids(20001) [ DB.ElementId ] >>> to_element_ids([20001, 20003]) [ DB.ElementId, DB.ElementId ]
Parameters: elements ( DB.Element
) – Iterable list (list
orset
) or single ofElement
,int
.Returns: List of Element Ids. Return type: [ DB.ElementId
, … ]
-
rpw.utils.coerce.
to_elements
(element_references, doc=Document)¶ Coerces element reference (
int
, orElementId
) intoDB.Element
. Remains unchanged if it’s alreadyDB.Element
. Accepts single object or lists.>>> from rpw.utils.coerce import to_elements >>> to_elements(DB.ElementId) [ DB.Element ] >>> to_elements(20001) [ DB.Element ] >>> to_elements([20001, 20003]) [ DB.Element, DB.Element ]
Parameters: element_references ([ DB.ElementId
,int
,DB.Element
]) – Element Reference, single or listReturns: Elements Return type: [ DB.Element
]
-
rpw.utils.coerce.
to_iterable
(item_or_iterable)¶ Ensures input is iterable
>>> from rpw.utils.coerce import to_iterable >>> to_iterable(SomeElement) [SomeElement]
Parameters: any (iterable, non-iterable) – Returns: Same as input Return type: (iterable)
-
rpw.utils.coerce.
to_pascal_case
(snake_str)¶ Converts Snake Case to Pascal Case
>>> to_pascal_case('family_name') 'FamilyName'
Mixins¶
Collection of Class Mixins
-
class
rpw.utils.mixins.
ByNameCollectMixin
¶ Adds name, by_name(), and by_name_or_element_ref() methods. This is for class inheritance only, used to reduce duplication
-
classmethod
by_name
(name)¶ Mixin to provide instantiating by a name for classes that are collectible. This is a mixin so specifi usage will vary for each for. This method will call the
rpw.db.Element.collect
method of the class, and return the first element with a matching.name
property.>>> LinePatternElement.by_name('Dash') <rpw:LinePatternElement name:Dash>
>>> FillPatternElement.by_name('Solid') <rpw:FillPatternElement name:Solid>
-
classmethod
by_name_or_element_ref
(reference)¶ Mixin for collectible elements. This is to help cast elements from name, elemente, or element_id
-
name
¶ Returns object’s Name attribute
-
classmethod
Logger¶
Rpw Logger
Usage:
>>> from rpw.utils.logger import logger
>>> logger.info('My logger message')
>>> logger.error('My error message')
-
class
rpw.utils.logger.
LoggerWrapper
¶ Logger Wrapper to extend loggers functionality. The logger is called in the same as the regular python logger, but also as a few extra features.
>>> logger.info('Message') [INFO] Message
Log Title
>>> logger.title('Message') ========= Message =========
Disable logger
>>> logger.disable()
Log Errors: This method appends errmsg to self.errors. This allows you to check if an error occured, and if it did not, close console window.
>>> logger.error('Message') [ERROR] Message >>> print(logger.errors) ['Message']
-
critical
(msg)¶ Log Message on logging.CRITICAL level
-
debug
(msg)¶ Log Message on logging.DEBUG level
-
disable
()¶ Sets logger level to logging.CRICITAL
-
error
(msg)¶ Log Message on logging.ERROR level
-
info
(msg)¶ Log Message on logging.INFO level
-
title
(msg)¶ Log Message on logging.INFO level with lines above and below
-
verbose
(verbose)¶ Sets logger to Verbose.
Parameters: (bool) – True to set logger.DEBUG, False to set to logging.INFO. - Usage:
>>> logger.verbose(True)
-
warning
(msg)¶ Log Message on logging.WARNING level
-
.Net¶
.NET imports
This module ensures most commonly used .NET classes are loaded for you.for
>>> from rpw.utils.dotnet import List, Enum, Process
Implementation¶
Coerce
"""
Type Casting Utilities
"""
import rpw
from rpw import revit, DB
from rpw.base import BaseObjectWrapper
from rpw.db.builtins import BicEnum
from rpw.utils.dotnet import List
from rpw.exceptions import RpwTypeError
def to_element_id(element_reference):
"""
Coerces Element References (Element, ElementId, ...) to Element Id
>>> from rpw.utils.coerce import to_element_id
>>> to_element_id(SomeElement)
<Element Id>
"""
if hasattr(element_reference, 'Id'):
element_id = element_reference.Id
elif isinstance(element_reference, DB.Reference):
element_id = element_reference.ElementId
elif isinstance(element_reference, int):
element_id = DB.ElementId(element_reference)
elif isinstance(element_reference, DB.ElementId):
element_id = element_reference
elif element_reference == DB.ElementId.InvalidElementId:
element_id = element_reference
else:
raise RpwTypeError('Element, ElementId, or int', type(element_reference))
return element_id
def to_element_ids(element_references):
"""
Coerces an element or list of elements into element ids.
Elements remain unchanged.
This will always return a list, even if only one element is passed.
>>> from rpw.utils.coerce import to_element_ids
>>> to_element_ids(DB.Element)
[ DB.ElementId ]
>>> to_element_ids(20001)
[ DB.ElementId ]
>>> to_element_ids([20001, 20003])
[ DB.ElementId, DB.ElementId ]
Args:
elements (``DB.Element``): Iterable list (``list`` or ``set``)
or single of ``Element``, ``int``.
Returns:
[``DB.ElementId``, ... ]: List of Element Ids.
"""
element_references = to_iterable(element_references)
return [to_element_id(e_ref) for e_ref in element_references]
# TODO: Add case to unwrap rpw elements
def to_element(element_reference, doc=revit.doc):
""" Same as to_elements but for a single object """
if isinstance(element_reference, DB.Element):
element = element_reference
elif isinstance(element_reference, DB.ElementId):
element = doc.GetElement(element_reference)
elif isinstance(element_reference, DB.Reference):
element = doc.GetElement(element_reference)
elif isinstance(element_reference, int):
element = doc.GetElement(DB.ElementId(element_reference))
elif hasattr(element_reference, 'unwrap'):
element = element_reference.unwrap()
else:
raise RpwTypeError('Element, ElementId, or int', type(element_reference))
return element
def to_elements(element_references, doc=revit.doc):
"""
Coerces element reference (``int``, or ``ElementId``) into ``DB.Element``.
Remains unchanged if it's already ``DB.Element``.
Accepts single object or lists.
>>> from rpw.utils.coerce import to_elements
>>> to_elements(DB.ElementId)
[ DB.Element ]
>>> to_elements(20001)
[ DB.Element ]
>>> to_elements([20001, 20003])
[ DB.Element, DB.Element ]
Args:
element_references ([``DB.ElementId``, ``int``, ``DB.Element``]): Element Reference,
single or list
Returns:
[``DB.Element``]: Elements
"""
element_references = to_iterable(element_references)
return [to_element(e_ref) for e_ref in element_references]
def to_class(class_reference):
""" Coerces a class or class reference to a Class.
>>> from rpw.utils.coerce import to_class
>>> to_class('Wall')
[ DB.Wall ]
>>> to_class(Wall)
[ DB.Wall ]
Args:
class_reference ([``DB.Wall``, ``str``]): Class Reference or class name
Returns:
[``type``]: Class
"""
if isinstance(class_reference, str):
return getattr(DB, class_reference)
if isinstance(class_reference, type):
return class_reference
raise RpwTypeError('Class Type, Class Type Name', type(class_reference))
def to_category(category_reference, fuzzy=True):
""" Coerces a category, category name or category id to a BuiltInCategory.
>>> from rpw.utils.coerce import to_category
>>> to_category('OST_Walls')
BuiltInCategory.OST_Walls
>>> to_category('Walls')
BuiltInCategory.OST_Walls
>>> to_category(BuiltInCategory.OST_Walls)
BuiltInCategory.OST_Walls
Args:
cateagory_reference ([``DB.BuiltInCategory``, ``str``, ``CategoryId``]): Category Reference
or Name
Returns:
[``BuiltInCategory``]: BuiltInCategory
"""
if isinstance(category_reference, DB.BuiltInCategory):
return category_reference
if isinstance(category_reference, str):
if fuzzy:
return BicEnum.fuzzy_get(category_reference)
else:
return BicEnum.get(category_reference)
if isinstance(category_reference, DB.ElementId):
return BicEnum.from_category_id(category_reference)
raise RpwTypeError('Category Type, Category Type Name',
type(category_reference))
def to_category_id(category_reference, fuzzy=True):
"""
Coerces a category, category name or category id to a Category Id.
>>> from rpw.utils.coerce import to_category_id
>>> to_category_id('OST_Walls')
<ElementId>
>>> to_category_id('Wall')
<ElementId>
>>> to_category_id(BuiltInCategory.OST_Walls)
<ElementId>
Args:
cateagory_reference ([``DB.BuiltInCategory``, ``str``, ``CategoryId``]): Category Reference
or Name
Returns:
[``DB.ElementId``]: ElementId of Category
"""
category_enum = to_category(category_reference)
return DB.ElementId(category_enum)
def to_iterable(item_or_iterable):
"""
Ensures input is iterable
>>> from rpw.utils.coerce import to_iterable
>>> to_iterable(SomeElement)
[SomeElement]
Args:
any (iterable, non-iterable)
Returns:
(`iterable`): Same as input
"""
if hasattr(item_or_iterable, '__iter__'):
return item_or_iterable
else:
return [item_or_iterable]
def to_pascal_case(snake_str):
""" Converts Snake Case to Pascal Case
>>> to_pascal_case('family_name')
'FamilyName'
"""
components = snake_str.split('_')
return "".join(x.title() for x in components)
# def dictioary_to_string(dictionary):
# """ Makes a string with key:value pairs from a dictionary
# >>> dictionary_to_string({'name': 'value'})
# 'name:value'
# >>> dictionary_to_string({'name': 'value', 'id':5})
# 'name:value id:5'
# """
# return ' '.join(['{0}:{1}'.format(k, v) for k, v in dictionary.iteritems()])
Mixins
""" Collection of Class Mixins """
import rpw
# from rpw import revit, db, DB # Fixes Circular Import
from rpw.exceptions import RpwCoerceError
from rpw.utils.logger import deprecate_warning
class ByNameCollectMixin():
""" Adds name, by_name(), and by_name_or_element_ref() methods.
This is for class inheritance only, used to reduce duplication
"""
@property
def name(self):
""" Returns object's Name attribute """
return self._revit_object.Name
@classmethod
def by_name(cls, name):
"""
Mixin to provide instantiating by a name for classes that are
collectible. This is a mixin so specifi usage will vary for each for.
This method will call the :any:`rpw.db.Element.collect`
method of the class, and return the first element with a
matching ``.name`` property.
>>> LinePatternElement.by_name('Dash')
<rpw:LinePatternElement name:Dash>
>>> FillPatternElement.by_name('Solid')
<rpw:FillPatternElement name:Solid>
"""
e = cls.collect(where=lambda e: e.name.lower() == name.lower()).get_first()
if e:
return e
raise RpwCoerceError('by_name({})'.format(name), cls)
@classmethod
def by_name_or_element_ref(cls, reference):
"""
Mixin for collectible elements.
This is to help cast elements from name, elemente, or element_id
"""
if isinstance(reference, str):
return cls.by_name(reference)
elif isinstance(reference, rpw.DB.ElementId):
return rpw.db.Element.from_id(reference)
else:
return cls(reference)
class CategoryMixin():
""" Adds category and get_category methods.
"""
@property
def _category(self):
"""
Default Category Access Parameter. Overwrite on wrapper as needed.
See Family Wrapper for an example.
"""
return self._revit_object.Category
@property
def category(self):
""" Wrapped ``DB.Category`` """
deprecate_warning('.category', 'get_category()')
return rpw.db.Category(self._category)
def get_category(self, wrapped=True):
""" Wrapped ``DB.Category``"""
return rpw.db.Category(self._category) if wrapped else self._category
Logger
"""
Rpw Logger
Usage:
>>> from rpw.utils.logger import logger
>>> logger.info('My logger message')
>>> logger.error('My error message')
"""
import sys
class mockLoggerWrapper():
def __init__(*args, **kwargs):
pass
def __getattr__(self, *args, **kwargs):
return mockLoggerWrapper(*args, **kwargs)
def __call__(self, *args, **kwargs):
pass
class LoggerWrapper():
"""
Logger Wrapper to extend loggers functionality.
The logger is called in the same as the regular python logger,
but also as a few extra features.
>>> logger.info('Message')
[INFO] Message
Log Title
>>> logger.title('Message')
=========
Message
=========
Disable logger
>>> logger.disable()
Log Errors: This method appends errmsg to self.errors.
This allows you to check if an error occured, and if it did not,
close console window.
>>> logger.error('Message')
[ERROR] Message
>>> print(logger.errors)
['Message']
"""
def __init__(self):
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter("[%(levelname)s] %(message)s")
# TODO: Show Module
# formatter = logging.Formatter("[%(levelname)s] %(message)s [%(module)s:%(lineno)s]")
handler.setFormatter(formatter)
logger = logging.getLogger('rpw_logger')
logger.addHandler(handler)
logger.setLevel(logging.INFO)
handler_title = logging.StreamHandler(sys.stdout)
formatter_title = logging.Formatter("%(message)s")
handler_title.setFormatter(formatter_title)
logger_title = logging.getLogger('rpw_logger_title')
logger_title.addHandler(handler_title)
logger_title.setLevel(logging.INFO)
self._logger = logger
self._logger_title = logger_title
self.errors = []
def disable(self):
""" Sets logger level to logging.CRICITAL """
self._logger.setLevel(logging.CRITICAL)
def verbose(self, verbose):
"""
Sets logger to Verbose.
Args:
(bool): True to set `logger.DEBUG`, False to set to `logging.INFO`.
Usage:
>>> logger.verbose(True)
"""
if verbose:
self._logger.setLevel(logging.DEBUG)
else:
self._logger.setLevel(logging.INFO)
def title(self, msg):
""" Log Message on logging.INFO level with lines above and below """
print('=' * 100)
self._logger_title.info(msg)
print('=' * 100)
def info(self, msg):
""" Log Message on logging.INFO level """
self._logger.info(msg)
def debug(self, msg):
""" Log Message on logging.DEBUG level """
self._logger.debug(msg)
def warning(self, msg):
""" Log Message on logging.WARNING level """
self._logger.warning(msg)
def error(self, msg):
""" Log Message on logging.ERROR level """
self._logger.error(msg)
self.errors.append(msg)
def critical(self, msg):
""" Log Message on logging.CRITICAL level """
self._logger.critical(msg)
def setLevel(self, level):
self._logger.setLevel(level)
def deprecate_warning(depracated, replaced_by=None):
msg = '{} has been deprecated and will be removed soon.'.format(depracated)
if replaced_by:
msg += ' Use {} instead'.format(replaced_by)
logger.warning(msg)
try:
import logging
except ImportError:
# In Dynamo, Use Mock Logger
logger = mockLoggerWrapper()
else:
# In PyRevit, Use Logger
logger = LoggerWrapper()
.NET
"""
.NET imports
This module ensures most commonly used .NET classes are loaded for you.for
>>> from rpw.utils.dotnet import List, Enum, Process
"""
import sys
from rpw.utils.logger import logger
from rpw.utils.sphinx_compat import MockImporter
# Attempt to Import clr
try:
import clr
except ImportError:
# Running Sphinx. Import MockImporter
logger.warning('Error Importing CLR. Loading Mock Importer')
sys.meta_path.append(MockImporter())
################
# .NET IMPORTS #
################
import clr
clr.AddReference('System') # Enum, Diagnostics
clr.AddReference('System.Collections') # List
# Core Imports
from System import Enum
from System.Collections.Generic import List
from System.Diagnostics import Process