Honeybee Components Crash Rhino Immediately (Python uuid dependency seems suspect)

@eirannejad

I ran that code on the problematic machine finding that there is no entry for Dynamic Assemblies. That’s primarily a .Net interface isn’t it?

Name,Version,Location
IronPython,2.7.8.0,C:\Program Files\Rhino 6\Plug-ins\IronPython\IronPython.dll
IronPython.Modules,2.7.8.0,C:\Program Files\Rhino 6\Plug-ins\IronPython\IronPython.Modules.dll

Nah I think that assembly is generated dyanically by the python code editor window. My test was from Rhino 7 (that I think might use a different editor or have more functionality). Rhino 6 reports the same as yours

Name,Version,Location
IronPython,2.7.8.0,C:\Program Files\Rhino 6\Plug-ins\IronPython\IronPython.dll
IronPython.Modules,2.7.8.0,C:\Program Files\Rhino 6\Plug-ins\IronPython\IronPython.Modules.dll

@Paul1 Okay let’s try running this now and see the output

import ctypes
print ctypes.util.find_library('rpcrt4')

_UuidCreate = None
try:
    import ctypes, ctypes.util
    import sys, os

    try:
        lib = ctypes.windll.rpcrt4
        print lib
    except:
        lib = None
    _UuidCreate = getattr(lib, 'UuidCreateSequential', getattr(lib, 'UuidCreate', None))
except Exception as ex:
    print(str(ex))
C:\WINDOWS\system32\rpcrt4.dll
<WinDLL 'rpcrt4', handle db830000 at 6c>

@eirannejad

On the problem machine that code returns the following error. The temp file does exist containing a copy of the script entered into the Rhino Python Editor.

Message: ‘module’ object has no attribute ‘util’
Traceback:
line 2, in , “C:\Users\PDrake\AppData\Local\Temp\TempScript.py”

On a machine able to use uuid, I get info similar to your’s.

C:\windows\system32\rpcrt4.dll
<WinDLL ‘rpcrt4’, handle fd0c0000 at 4ab9c>

@Paul1 Okay try this now (added an extra line from ctypes.util import find_library to avoid the module error)

import ctypes
from ctypes.util import find_library
print find_library('rpcrt4')

_UuidCreate = None
try:
    import ctypes, ctypes.util
    import sys, os

    try:
        lib = ctypes.windll.rpcrt4
        print lib
    except:
        lib = None
    _UuidCreate = getattr(lib, 'UuidCreateSequential', getattr(lib, 'UuidCreate', None))
except Exception as ex:
    print(str(ex))

@eirannejad
Thanks for updating - that does load ctypes and outputs the library location correctly:

C:\WINDOWS\system32\rpcrt4.dll
<WinDLL ‘rpcrt4’, handle c9d70000 at 6c>

Odd. Okay open this file, copy the contents and run in the python editor please:
C:\Program Files\Rhino 6\Plug-ins\IronPython\Lib\uuid.py

If this doesn’t cause a crash, then we need to dig a lot deeper and I’ll probably need some sort of access to the bad machine. Maybe we can use TeamViewer

Running the script directly in the Python editor does crash Rhino all the same as importing uuid.

I’ll try commenting some out to see if I can narrow down why.

@Paul1 Okay is there a way I can have access to the machine (using TeamViewer) to test a few things out and maybe figure out the issue?

2 Likes

Yeah! If you have a TeamViewer license we may be able to. Perhaps Microsoft Teams could would work fine as well.

@eirannejad I’m having the same issues as @Paul1. Let me know if there is anything I can do to help the two of you troubleshoot this issue. Thanks

1 Like

So just an update - IT added exceptions for the following folders and there was no change. Am I possibly missing a directory for the system libraries like rpcrt4.dll?

C:\Python27_64
C:\Program Files\IronPython 2.7
C:\Program Files\Rhino 6\Plugins\IronPython
C:\Users\username\ladybug_tools\python
C:\Users\username\AppData\Roaming\Grasshopper\

@alexchapin does your computer have SentinelOne for antivirus?

Another thread I found that might be of interest for this issue.

Thank you for sharing this other link and yes my computer uses SentinelOne for antivirus. Luckily I was finally able to solve my issue by having IT include the folder path for the Rhino.exe file in the exclusion list.

3 Likes

@alexchapin Thanks for reporting back. I’ll see if adding the Rhino folder works.

@eirannejad If IT whitelists the Rhino folder, that seems odd still in this sense that uuid.py couldn’t run correctly even in Powershell.

1 Like

It’s possible that my issue was different from yours. I’m not sure. Hopefully my experience can help you though. Good luck.

Having IT add an exception for the Rhino folder allows uuid dependency to work again on that machine. Discussing it with IT, they also mentioned they modified a few overall settings in S1 to handle error suppression in a different way that doesn’t just ignore exceptions.

2 Likes

I´m having the very same issue

I confirm import UUID make Rhino crash if I create a separate gh component.

I´ running Version 6 SR33 (6.33.20343.16431, 08/12/2020)

IronPython - shall I update to a verison later than 2.7.8

@eirannejad I can´t find any Microsoft.Dynamic.dll if I search in the PC. The one on Rhino 6 it´s on my pc on the location above. I do not have Ironbug either.

How can I solve the issue?

Hi @Paul1 ,
I’m experiencing a similar issue.
Can you be more specific on what kind of overall settings your IT team did in the Sentinel one?

Hi, I had the same issue on Rhino 7 (IronPython,2.7.12.0,C:\Program Files\Rhino 7\Plug-ins\IronPython\IronPython.dll),

but not for Rhino 6 on the same machine (IronPython,2.7.8.0,C:\Program Files\Rhino 6\Plug-ins\IronPython\IronPython.dll)

But rather than trying to fix, I instead just did the following, which will work if you don’t need method1 uuid generation which relies on obtaining the MAC address of a machine.

Basically copy the UUID class from uuid.py along with the three mehods uuid3, 4 and 5 and the namespace constants. (NOTE: most implementations of uuid generation use method 4)

"""Provides a scripting component.
    Inputs:
        x: The x string to be hashed
        y: The y string to be namespaced (currently not an input as this hardcodes the standard DNS namespace into the def for uuid3 and 5)
    Output:
        guid3: Method 3 guid result
        guid5: Method 5 guid result
    Remarks:
        __author__ = 'Ka-Ping Yee <ping@zesty.ca>' a simplified version of the uuid library that solely depends on hashlib as other libraries were causing errors in IronPython grasshopper"""
        
        
__author__ = "JulesBuh"
__version__ = "2022.04.26"



RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
    'reserved for NCS compatibility', 'specified in RFC 4122',
    'reserved for Microsoft compatibility', 'reserved for future definition']
class UUID(object):
    """Instances of the UUID class represent UUIDs as specified in RFC 4122.
    UUID objects are immutable, hashable, and usable as dictionary keys.
    Converting a UUID to a string with str() yields something in the form
    '12345678-1234-1234-1234-123456789abc'.  The UUID constructor accepts
    five possible forms: a similar string of hexadecimal digits, or a tuple
    of six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and
    48-bit values respectively) as an argument named 'fields', or a string
    of 16 bytes (with all the integer fields in big-endian order) as an
    argument named 'bytes', or a string of 16 bytes (with the first three
    fields in little-endian order) as an argument named 'bytes_le', or a
    single 128-bit integer as an argument named 'int'.

    UUIDs have these read-only attributes:

        bytes       the UUID as a 16-byte string (containing the six
                    integer fields in big-endian byte order)

        bytes_le    the UUID as a 16-byte string (with time_low, time_mid,
                    and time_hi_version in little-endian byte order)

        fields      a tuple of the six integer fields of the UUID,
                    which are also available as six individual attributes
                    and two derived attributes:

            time_low                the first 32 bits of the UUID
            time_mid                the next 16 bits of the UUID
            time_hi_version         the next 16 bits of the UUID
            clock_seq_hi_variant    the next 8 bits of the UUID
            clock_seq_low           the next 8 bits of the UUID
            node                    the last 48 bits of the UUID

            time                    the 60-bit timestamp
            clock_seq               the 14-bit sequence number

        hex         the UUID as a 32-character hexadecimal string

        int         the UUID as a 128-bit integer

        urn         the UUID as a URN as specified in RFC 4122

        variant     the UUID variant (one of the constants RESERVED_NCS,
                    RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)

        version     the UUID version number (1 through 5, meaningful only
                    when the variant is RFC_4122)
    """

    def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None,
                       int=None, version=None):
        r"""Create a UUID from either a string of 32 hexadecimal digits,
        a string of 16 bytes as the 'bytes' argument, a string of 16 bytes
        in little-endian order as the 'bytes_le' argument, a tuple of six
        integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
        8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
        the 'fields' argument, or a single 128-bit integer as the 'int'
        argument.  When a string of hex digits is given, curly braces,
        hyphens, and a URN prefix are all optional.  For example, these
        expressions all yield the same UUID:

        UUID('{12345678-1234-5678-1234-567812345678}')
        UUID('12345678123456781234567812345678')
        UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
        UUID(bytes='\x12\x34\x56\x78'*4)
        UUID(bytes_le='\x78\x56\x34\x12\x34\x12\x78\x56' +
                      '\x12\x34\x56\x78\x12\x34\x56\x78')
        UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
        UUID(int=0x12345678123456781234567812345678)

        Exactly one of 'hex', 'bytes', 'bytes_le', 'fields', or 'int' must
        be given.  The 'version' argument is optional; if given, the resulting
        UUID will have its variant and version set according to RFC 4122,
        overriding the given 'hex', 'bytes', 'bytes_le', 'fields', or 'int'.
        """

        if [hex, bytes, bytes_le, fields, int].count(None) != 4:
            raise TypeError('need one of hex, bytes, bytes_le, fields, or int')
        if hex is not None:
            hex = hex.replace('urn:', '').replace('uuid:', '')
            hex = hex.strip('{}').replace('-', '')
            if len(hex) != 32:
                raise ValueError('badly formed hexadecimal UUID string')
            int = long(hex, 16)
        if bytes_le is not None:
            if len(bytes_le) != 16:
                raise ValueError('bytes_le is not a 16-char string')
            bytes = (bytes_le[3] + bytes_le[2] + bytes_le[1] + bytes_le[0] +
                     bytes_le[5] + bytes_le[4] + bytes_le[7] + bytes_le[6] +
                     bytes_le[8:])
        if bytes is not None:
            if len(bytes) != 16:
                raise ValueError('bytes is not a 16-char string')
            int = long(('%02x'*16) % tuple(map(ord, bytes)), 16)
        if fields is not None:
            if len(fields) != 6:
                raise ValueError('fields is not a 6-tuple')
            (time_low, time_mid, time_hi_version,
             clock_seq_hi_variant, clock_seq_low, node) = fields
            if not 0 <= time_low < 1<<32L:
                raise ValueError('field 1 out of range (need a 32-bit value)')
            if not 0 <= time_mid < 1<<16L:
                raise ValueError('field 2 out of range (need a 16-bit value)')
            if not 0 <= time_hi_version < 1<<16L:
                raise ValueError('field 3 out of range (need a 16-bit value)')
            if not 0 <= clock_seq_hi_variant < 1<<8L:
                raise ValueError('field 4 out of range (need an 8-bit value)')
            if not 0 <= clock_seq_low < 1<<8L:
                raise ValueError('field 5 out of range (need an 8-bit value)')
            if not 0 <= node < 1<<48L:
                raise ValueError('field 6 out of range (need a 48-bit value)')
            clock_seq = (clock_seq_hi_variant << 8L) | clock_seq_low
            int = ((time_low << 96L) | (time_mid << 80L) |
                   (time_hi_version << 64L) | (clock_seq << 48L) | node)
        if int is not None:
            if not 0 <= int < 1<<128L:
                raise ValueError('int is out of range (need a 128-bit value)')
        if version is not None:
            if not 1 <= version <= 5:
                raise ValueError('illegal version number')
            # Set the variant to RFC 4122.
            int &= ~(0xc000 << 48L)
            int |= 0x8000 << 48L
            # Set the version number.
            int &= ~(0xf000 << 64L)
            int |= version << 76L
        self.__dict__['int'] = int

    def __cmp__(self, other):
        if isinstance(other, UUID):
            return cmp(self.int, other.int)
        return NotImplemented

    def __hash__(self):
        return hash(self.int)

    def __int__(self):
        return self.int

    def __repr__(self):
        return 'UUID(%r)' % str(self)

    def __setattr__(self, name, value):
        raise TypeError('UUID objects are immutable')

    def __str__(self):
        hex = '%032x' % self.int
        return '%s-%s-%s-%s-%s' % (
            hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:])

    def get_bytes(self):
        bytes = ''
        for shift in range(0, 128, 8):
            bytes = chr((self.int >> shift) & 0xff) + bytes
        return bytes

    bytes = property(get_bytes)

    def get_bytes_le(self):
        bytes = self.bytes
        return (bytes[3] + bytes[2] + bytes[1] + bytes[0] +
                bytes[5] + bytes[4] + bytes[7] + bytes[6] + bytes[8:])

    bytes_le = property(get_bytes_le)

    def get_fields(self):
        return (self.time_low, self.time_mid, self.time_hi_version,
                self.clock_seq_hi_variant, self.clock_seq_low, self.node)

    fields = property(get_fields)

    def get_time_low(self):
        return self.int >> 96L

    time_low = property(get_time_low)

    def get_time_mid(self):
        return (self.int >> 80L) & 0xffff

    time_mid = property(get_time_mid)

    def get_time_hi_version(self):
        return (self.int >> 64L) & 0xffff

    time_hi_version = property(get_time_hi_version)

    def get_clock_seq_hi_variant(self):
        return (self.int >> 56L) & 0xff

    clock_seq_hi_variant = property(get_clock_seq_hi_variant)

    def get_clock_seq_low(self):
        return (self.int >> 48L) & 0xff

    clock_seq_low = property(get_clock_seq_low)

    def get_time(self):
        return (((self.time_hi_version & 0x0fffL) << 48L) |
                (self.time_mid << 32L) | self.time_low)

    time = property(get_time)

    def get_clock_seq(self):
        return (((self.clock_seq_hi_variant & 0x3fL) << 8L) |
                self.clock_seq_low)

    clock_seq = property(get_clock_seq)

    def get_node(self):
        return self.int & 0xffffffffffff

    node = property(get_node)

    def get_hex(self):
        return '%032x' % self.int

    hex = property(get_hex)

    def get_urn(self):
        return 'urn:uuid:' + str(self)

    urn = property(get_urn)

    def get_variant(self):
        if not self.int & (0x8000 << 48L):
            return RESERVED_NCS
        elif not self.int & (0x4000 << 48L):
            return RFC_4122
        elif not self.int & (0x2000 << 48L):
            return RESERVED_MICROSOFT
        else:
            return RESERVED_FUTURE

    variant = property(get_variant)

    def get_version(self):
        # The version bits are only meaningful for RFC 4122 UUIDs.
        if self.variant == RFC_4122:
            return int((self.int >> 76L) & 0xf)

    version = property(get_version)
NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8')
NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')
NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')

def uuid3(namespace, name):
    """Generate a UUID from the MD5 hash of a namespace UUID and a name."""
    from hashlib import md5
    hash = md5(namespace.bytes + name).digest()
    return UUID(bytes=hash[:16], version=3)

def uuid4():
    """Generate a random UUID."""
    from os import urandom
    return UUID(bytes=urandom(16), version=4)
    
def uuid5(namespace, name):
    """Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
    from hashlib import sha1
    hash = sha1(namespace.bytes + name).digest()
    return UUID(bytes=hash[:16], version=5)


guid3 = uuid3(NAMESPACE_DNS,x).__str__()
guid4 = uuid4().__str__()
guid5 = uuid5(NAMESPACE_DNS,x).__str__()

1 Like