pyrevitlabs / pyRevit

Rapid Application Development (RAD) Environment for Autodesk Revit®
http://wiki.pyrevitlabs.io
GNU General Public License v3.0
1.28k stars 332 forks source link

CPython Interface Implementation failed #686

Closed htlcnn closed 5 years ago

htlcnn commented 5 years ago

Describe the bug I implemented an ISelectionFilter interface in my script and use it for uidoc.Selection.PickObjects. Here is my script:

#! python3
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import UIDocument, Selection
from Autodesk.Revit.Exceptions import OperationCanceledException

from System.Collections.Generic import List

import os
import tempfile

from PIL import Image

uiapp = __revit__
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application
doc = uidoc.Document

import time

class CategoriesFilter(Selection.ISelectionFilter):
    # https://stackoverflow.com/a/50462691
    __namespace__ = 'HTL %s' % int(time.time())
    # __namespace__ = 'HTL'
    def __init__(self, names):
        self.names = names
    def AllowElement(self, element):
        return element.Category.Name in self.names

def select_objects_by_category(*names):
    prompt = 'Pick {}'.format(', '.join(names))
    references = uidoc.Selection.PickObjects(Selection.ObjectType.Element,
                                           CategoriesFilter(names), prompt)
    return [doc.GetElement(reference) for reference in references]

def select_object_by_category(name):
    prompt = 'Pick {}'.format(name)
    reference = uidoc.Selection.PickObject(Selection.ObjectType.Element,
                                           CategoriesFilter(name), prompt)
    return doc.GetElement(reference)

def main():
    rebars = select_objects_by_category('Structural Rebar')
    print(rebars)

try:
    main()
except OperationCanceledException:
    pass

Note that in order to run the script without exception, I had to append time.time() to my class' __namespace__. If I didn't do that, I would get this exception:

CPython Traceback:
TypeError : Duplicate type name within an assembly.
 File "E:\Setup\UCE\Autodesk\Revit\pyRevit extensions\htl.extension\HTL Rebar.tab\Schedule.panel\CreateRebarImage.pushbutton\script.py", line 23, in <module>
class CategoriesFilter(Selection.ISelectionFilter):

pyRevitLabs.PythonNet
at pyRevitLabs.PythonNet.Runtime.CheckExceptionOccurred()
 at pyRevitLabs.PythonNet.PythonEngine.RunString(String code, Nullable`1 globals, Nullable`1 locals, RunFlagType flag, Encoding encoding)
 at pyRevitLabs.PythonNet.PythonEngine.ExecUTF8(String code, Nullable`1 globals, Nullable`1 locals)
 at PyRevitLabs.PyRevit.Runtime.CPythonEngine.Execute(ScriptRuntime& runtime)

To Reproduce Steps to reproduce the behavior:

  1. Run the script above with the line __namespace__ = 'HTL' uncommented
  2. Run the script again

Expected behavior Script run without exception. Is there any method to avoid recreation of classes (or to override them) if they already exist in certain namespace?

Desktop

C:\Users\HTL>pyrevit env
==> Registered Clones (full git repos)
==> Registered Clones (deployed from archive/image)
master | Deploy: "basepublic" | Branch: "master" | Version: "4.7-beta2" | Path: "C:\Users\HTL\AppData\Roaming\pyRevit-Master"
==> Attachments
master | Product: "2020.1 Update" | Engine: 277 | Path: "C:\Users\HTL\AppData\Roaming\pyRevit-Master" | Manifest: "C:\Users\HTL\AppData\Roaming\Autodesk\Revit\Addins\2020\pyRevit.addin"
master | Product: "2018.3.1" | Engine: 277 | Path: "C:\Users\HTL\AppData\Roaming\pyRevit-Master" | Manifest: "C:\Users\HTL\AppData\Roaming\Autodesk\Revit\Addins\2018\pyRevit.addin"
==> Installed Extensions
htl | Type: Unknown | Repo: "git@gitlab.com:hoangthanhlong/pyrevitscripts.git" | Installed: "E:\Setup\UCE\Autodesk\Revit\pyRevit extensions\htl.extension"
==> Default Extension Search Path
C:\Users\HTL\AppData\Roaming\pyRevit\Extensions
==> Extension Search Paths
E:\Setup\UCE\Autodesk\Revit\pyRevit extensions
D:\HTL\Desktop
==> Extension Sources - Default
https://github.com/eirannejad/pyRevit/raw/master/extensions/extensions.json
==> Extension Sources - Additional
==> Installed Revits
2020.1 Update | Version: 20.1.0.81 | Build: 20190725_1135(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2020"
2018.3.1 | Version: 18.3.1.2 | Build: 20180423_1000(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2018"
==> Running Revit Instances
PID: 10920 | 2020.1 Update | Version: 20.1.0.81 | Build: 20190725_1135(x64) | Language: 0 | Path: "C:\Program Files\Autodesk\Revit 2020"
==> User Environment
Microsoft Windows 10 [Version 10.0.17134]
Executing User: HTL\HTL
Active User: HTL\HTL
Adming Access: No
%APPDATA%: "C:\Users\HTL\AppData\Roaming"
Latest Installed .Net Framework: 4.8
Installed .Net Target Packs: v3.5 v4.0 v4.5 v4.5.1 v4.5.2 v4.6 v4.6.1 v4.6.2 v4.7 v4.7.1 v4.7.2 v4.8 v4.X
Installed .Net-Core Target Packs: v2.1.602
pyRevit CLI 0.14.0.0
eirannejad commented 5 years ago

Great find with the __namespace__. I didn't know it's there. It would be great if you can write up your findings on using the CPython with Revit in a markdown file and I'll publish on the Wiki

I'm going to add a __timestamp__ global variable to the pyRevit engine. This way you can:

class CategoriesFilter(UI.Selection.ISelectionFilter):
    __namespace__ = __commanduniqueid__ + __timestamp__

Keep us posted on your findings please.

eirannejad commented 5 years ago

Okay implemented timestamp and execid (unique id for each execution). The code can be simplified as

from pyrevit import EXEC_PARAMS
class CategoriesFilter(UI.Selection.ISelectionFilter):
    __namespace__ = EXEC_PARAMS.exec_id

or less safer but with not pyrevit import

from pyrevit import EXEC_PARAMS
class CategoriesFilter(UI.Selection.ISelectionFilter):
    __namespace__ = __execid__

Will publish this soon with 4.7-beta3

htlcnn commented 5 years ago

I don't know much about underlying Python Engine of .NET. I just don't know if appending timestamp is proper way to do it, or it would polute the namespace or raise memory etc.

Anyways, I attach markdown writeup below:

cpython net interface implementation.zip

eirannejad commented 5 years ago

Send me your info so I can add as part of Community

htlcnn commented 5 years ago

I've emailed you my info. Thanks!

htlcnn commented 5 years ago

@eirannejad I've read your commit above (https://github.com/eirannejad/pyRevit/commit/0073bbc6072c771f6bb507aeb8068a23cc917c87), I'd like to add that IronPython works fine without __namespace__ defined.