Repository: pyrevitlabs/pyRevit
Pull request #2675: compat: Revit 2026, ElementId Int64
Author: login: jmcouffin
Labels: name: Backward Compatibility
description: Backward Compatibility Issue with Older Revit Versions
Created: 2025-05-23T08:01:35Z | Merged: 2025-05-23T08:59:19Z


# Compatibility issue 2026: Int64 new ElementID type

## Description

support for 2026 Int64 ElemenId

---

## Checklist

Before submitting your pull request, ensure the following requirements are met:

- [x] Code follows the [PEP 8](https://peps.python.org/pep-0008/) style guide.
- [x] Code has been formatted with [Black](https://github.com/psf/black) using the command:
  ```bash
  pipenv run black {source_file_or_directory}
  ```
- [x] Changes are tested and verified to work as expected.

---

## Related Issues

If applicable, link the issues resolved by this pull request:

- Resolves #2666



Linked issue #2666: [Bug]: MRead doesn't work in Revit 2026
### ✈ Pre-Flight checks

- [x] I **don't have SentinelOne** antivirus installed (see above for the solution)
- [x] I have **searched in the issues** (open and closed) but couldn't find a similar issue
- [x] I have **searched in the [pyRevit Forum](https://discourse.pyrevitlabs.io)** for similar issues
- [x] I already **followed the [installation troubleshooting guide](https://pyrevitlabs.notion.site/Installation-issues-f6495adb56254e9e8021f8a68e3b2ab7) thoroughly**
- [x] I am using the **latest pyRevit Version**

### 🐞 Describe the bug

Using pyRevit v5.1.0, the MRead command does not select elements in Revit 2026.
The error is triggered by the line:
`element_ids.append(DB.ElementId(int(elid)))`
I resolved it by replacing int with System.Int64 (based on ChatGPT's suggestion), which requires importing the System module:
`import System
element_ids.append(DB.ElementId(System.Int64(elid)))`


### ⌨ Error/Debug Message

```shell
nothing happens
```

### ♻️ To Reproduce

1. select elements and run MWrite command
2. run MRead command

### ⏲️ Expected behavior

Elements from MWrite should have been selected in revit

### 🖥️ Hardware and Software Setup (please complete the following information)

```shell
==> Registered Clones (full git repos)
==> Registered Clones (deployed from archive/image)
master | Branch: "master" | Version: "5.1.0.25094+1131-wip" | Path: "C:\Program Files\pyRevit-Master"
==> Attachments
master | Product: "2026 First Customer Ship" | Engine: DEFAULT (2712) | Path: "C:\Program Files\pyRevit-Master" | AllUsers
master | Product: "2025 First Customer Ship" | Engine: DEFAULT (2712) | Path: "C:\Program Files\pyRevit-Master" | AllUsers
master | Product: "Autodesk Revit 2024" | Engine: DEFAULT (2712) | Path: "C:\Program Files\pyRevit-Master" | AllUsers
master | Product: "2022.1.8" | Engine: DEFAULT (2712) | Path: "C:\Program Files\pyRevit-Master" | AllUsers
master | Product: "2019.2.6" | Engine: DEFAULT (2712) | Path: "C:\Program Files\pyRevit-Master" | AllUsers
==> Installed Extensions
==> Default Extension Search Path
C:\Users\betsalel\AppData\Roaming\pyRevit\Extensions
==> Extension Search Paths
==> Extension Sources - Default
https://github.com/pyrevitlabs/pyRevit/raw/master/extensions/extensions.json
==> Extension Sources - Additional
==> Installed Revits
2026 First Customer Ship | Version: 26.0.4.409 | Build: 20250227_1515(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2026\"
2025 First Customer Ship | Version: 25.0.2.419 | Build: 20240307_1300(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2025\"
Autodesk Revit 2024 | Version: 24.3.20.34 | Build: 20250402_1515(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2024\"
2022.1.8 | Version: 22.1.80.32 | Build: 20241107_1515(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2022\"
2019.2.6 | Version: 19.2.60.3 | Build: 20220224_1700(x64) | Language: 1033 | Path: "C:\Program Files\Autodesk\Revit 2019\"
==> Running Revit Instances
==> User Environment
Microsoft Windows 10 [Version 10.0.22631]
Executing User: ****
Active User: ****
Admin Access: Yes
%APPDATA%: "C:\Users\****\AppData\Roaming"
Latest Installed .Net Framework: 8.0.11
No .Net Target Packs are installed.
No .Net-Core Target Packs are installed.
pyRevit CLI v5.1.0.25094+1131-wip.ffa32944090b8fb19834361c5750674555b2c0a2
```

### Additional context

_No response_
Linked issue comments: user: login: ciarancosgriff
created_at: 2025-05-23T00:39:04Z
body: The Revit API has deprecated ElementID(Int32). The script needs to be updated to use the ElementID(Int64) constructor.

I added this line to the imports:
from System import Int64

and updated the element_ids.append() method to:
element_ids.append(DB.ElementId(Int64(elid)))

Works without issue after that.
author_association: NONE
user: login: jmcouffin
created_at: 2025-05-23T08:59:33Z
body: Thanks @ciarancosgriff and @bll85
fixed by #2675
author_association: CONTRIBUTOR
user: login: jlucero1
created_at: 2025-06-24T12:04:46Z
body: Mread is working in Revit 2023 but not in 2026. I have seen the above solution, but I'm unsure of the steps I need to take to implement it. I have followed the instructions:

- I don't have SentinelOne antivirus installed (see above for the solution)
- I have searched in the issues (open and closed) but couldn't find a similar issue
- I have searched in the [pyRevit Forum](https://discourse.pyrevitlabs.io/) for similar issues
- I already followed the [installation troubleshooting guide](https://pyrevitlabs.notion.site/Installation-issues-f6495adb56254e9e8021f8a68e3b2ab7) thoroughly
- I am using the latest pyRevit Version
author_association: NONE
user: login: jmcouffin
created_at: 2025-06-24T13:23:56Z
body: > Mread is working in Revit 2023 but not in 2026.

no link to original issue, open your own.
author_association: CONTRIBUTOR

Diff:
diff --git a/extensions/pyRevitTools.extension/pyRevit.tab/Drawing Set.panel/views.stack/Schedules.pulldown/Create Schedule from CSV.pushbutton/script.py b/extensions/pyRevitTools.extension/pyRevit.tab/Drawing Set.panel/views.stack/Schedules.pulldown/Create Schedule from CSV.pushbutton/script.py
index 2b19e75612..1435f2d4d8 100644
--- a/extensions/pyRevitTools.extension/pyRevit.tab/Drawing Set.panel/views.stack/Schedules.pulldown/Create Schedule from CSV.pushbutton/script.py	
+++ b/extensions/pyRevitTools.extension/pyRevit.tab/Drawing Set.panel/views.stack/Schedules.pulldown/Create Schedule from CSV.pushbutton/script.py	
@@ -16,6 +16,7 @@
 import codecs
 import os.path as op
 from collections import defaultdict
+from System import Int64
 
 from pyrevit import PyRevitException
 from pyrevit import forms
@@ -66,7 +67,10 @@ def create_schedule_records(schedule, fields, records):
                     elif p.StorageType == DB.StorageType.String:
                         p.Set(record_data[idx])
                     elif p.StorageType == DB.StorageType.ElementId:
-                        p.Set(DB.ElementId(int(record_data[idx])))
+                        if HOST_APP.is_newer_than(2025):
+                            p.Set(DB.ElementId(Int64(record_data[idx])))
+                        else:
+                            p.Set(DB.ElementId(int(record_data[idx])))
 
 
 def has_matching_fields(keysched_def, fields):
@@ -107,8 +111,7 @@ def resolve_naming_conflicts(category, sched_name, fields, doc=None):
         (
             x
             for x in all_schedules
-            if x.Definition.IsKeySchedule
-            and x.Definition.CategoryId == category.Id
+            if x.Definition.IsKeySchedule and x.Definition.CategoryId == category.Id
         ),
         None,
     )
@@ -167,9 +170,7 @@ def create_key_schedule(
 
     # set name and key parameter name
     # fields[1:] because first field in keyname
-    new_name = resolve_naming_conflicts(
-        category, sched_name, fields[1:], doc=doc
-    )
+    new_name = resolve_naming_conflicts(category, sched_name, fields[1:], doc=doc)
     # if not name skip making schedule
     if not new_name:
         return
@@ -180,8 +181,7 @@ def create_key_schedule(
     new_key_sched.KeyScheduleParameterName = key_name
     # set the schedulable fields
     fsched_fields = {
-        x.GetName(doc): x
-        for x in new_key_sched.Definition.GetSchedulableFields()
+        x.GetName(doc): x for x in new_key_sched.Definition.GetSchedulableFields()
     }
     cancel_txn = False
     for field in fields:
@@ -219,8 +219,7 @@ def find_matching_keyschedule(category, fields, doc=None):
         (
             x
             for x in all_schedules
-            if x.Definition.IsKeySchedule
-            and x.Definition.CategoryId == category.Id
+            if x.Definition.IsKeySchedule and x.Definition.CategoryId == category.Id
         ),
         None,
     )
@@ -235,9 +234,7 @@ def update_key_schedule(keyschedule, category, fields, records, doc=None):
     if forms.alert(
         'key schedule "{}" has identical fields. Multiple key '
         "schedules can not set values for same parameter.\n"
-        "Do you want to update the data in existing schedule?".format(
-            keyschedule.Name
-        ),
+        "Do you want to update the data in existing schedule?".format(keyschedule.Name),
         yes=True,
         no=True,
     ):
@@ -292,8 +289,7 @@ def ensure_schedulable_parameters(category, parameter_defs, doc=None):
     with revit.DryTransaction(doc):
         temp_key_sched = DB.ViewSchedule.CreateKeySchedule(doc, category.Id)
         fsched_field_names = [
-            x.GetName(doc)
-            for x in temp_key_sched.Definition.GetSchedulableFields()
+            x.GetName(doc) for x in temp_key_sched.Definition.GetSchedulableFields()
         ]
 
         for param_def in parameter_defs:
@@ -394,9 +390,7 @@ def read_csv_typed_data(csv_file):
                     field_defs.append(
                         (
                             parts[0],
-                            coreutils.get_enum_value(
-                                DB.ParameterType, parts[1]
-                            ),
+                            coreutils.get_enum_value(DB.ParameterType, parts[1]),
                         )
                     )
     # return field definitions, and data
@@ -425,9 +419,7 @@ def read_csv_typed_data(csv_file):
                 category=key_sched_cat, fields=param_names
             )
             if existing_keyschedule:
-                with revit.Transaction(
-                    "Update Schedule from CSV", log_errors=False
-                ):
+                with revit.Transaction("Update Schedule from CSV", log_errors=False):
                     # update the data in current schedule
                     update_key_schedule(
                         keyschedule=existing_keyschedule,
@@ -437,9 +429,7 @@ def read_csv_typed_data(csv_file):
                         doc=revit.doc,
                     )
             else:
-                with revit.Transaction(
-                    "Create Schedule from CSV", log_errors=False
-                ):
+                with revit.Transaction("Create Schedule from CSV", log_errors=False):
                     # make sure field parameters exist
                     # creates project params if not
                     # skip the first on since it is "Key Name" and only applies
diff --git a/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/mema.stack/MRead.pushbutton/script.py b/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/mema.stack/MRead.pushbutton/script.py
index 28b10f2e78..b77e0d08d2 100644
--- a/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/mema.stack/MRead.pushbutton/script.py
+++ b/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/mema.stack/MRead.pushbutton/script.py
@@ -1,24 +1,27 @@
 import pickle
 
-from pyrevit.framework import List
 from pyrevit import script
-from pyrevit import revit, DB
-
+from pyrevit import revit, DB, HOST_APP
+from System import Int64
 
 selection = revit.get_selection()
-
+logger = script.get_logger()
 datafile = script.get_document_data_file("SelList", "pym")
 
-
 try:
-    f = open(datafile, 'rb')
+    f = open(datafile, "rb")
     current_selection = pickle.load(f)
     f.close()
 
     element_ids = []
-    for elid in current_selection:
-        element_ids.append(DB.ElementId(int(elid)))
+    if HOST_APP.is_newer_than(2025):
+        for elid in current_selection:
+            element_ids.append(DB.ElementId(Int64(elid)))
+    else:
+        for elid in current_selection:
+            element_ids.append(DB.ElementId(int(elid)))
 
     selection.set_to(element_ids)
-except Exception:
-    pass
+except Exception as e:
+    logger.debug("Error loading selection: %s" % e)
+    script.exit()
diff --git a/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/Memory.pulldown/Save Memory as Selection.pushbutton/script.py b/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/Memory.pulldown/Save Memory as Selection.pushbutton/script.py
index 9b2fb7a733..06d7fc9625 100644
--- a/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/Memory.pulldown/Save Memory as Selection.pushbutton/script.py	
+++ b/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/Memory.pulldown/Save Memory as Selection.pushbutton/script.py	
@@ -1,10 +1,12 @@
 """Saves current selection memory as a Selection Filter."""
-#pylint: disable=import-error,invalid-name,broad-except
+
+# pylint: disable=import-error,invalid-name,broad-except
 import os.path as op
 import pickle as pl
+from System import Int64
 
 from pyrevit import coreutils
-from pyrevit import revit, DB
+from pyrevit import revit, DB, HOST_APP
 from pyrevit import script
 
 logger = script.get_logger()
@@ -15,20 +17,20 @@
 
 if op.exists(data_file):
     if proj_info.name:
-        filter_name = \
-            'SavedSelection_' + proj_info.name + '_' + coreutils.timestamp()
+        filter_name = "SavedSelection_" + proj_info.name + "_" + coreutils.timestamp()
     else:
-        filter_name = \
-            'SavedSelection_' + coreutils.timestamp()
+        filter_name = "SavedSelection_" + coreutils.timestamp()
 
-    with open(data_file, 'rb') as f:
+    with open(data_file, "rb") as f:
         cursel = pl.load(f)
 
-    with revit.Transaction('pySaveSelection'):
-        selection_filter = \
-            DB.SelectionFilterElement.Create(
-                revit.doc,
-                coreutils.cleanup_filename(filter_name)
-                )
+    with revit.Transaction("pySaveSelection"):
+        selection_filter = DB.SelectionFilterElement.Create(
+            revit.doc, coreutils.cleanup_filename(filter_name)
+        )
+        is_version_newer_than2025 = HOST_APP.is_newer_than(2025)
         for elid in cursel:
-            selection_filter.AddSingle(DB.ElementId(int(elid)))
+            if is_version_newer_than2025:
+                selection_filter.AddSingle(DB.ElementId(Int64(elid)))
+            else:
+                selection_filter.AddSingle(DB.ElementId(int(elid)))
diff --git a/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/Memory.pulldown/lib/iter_selection.py b/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/Memory.pulldown/lib/iter_selection.py
index 5bfcfa6c4e..bc6406771f 100644
--- a/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/Memory.pulldown/lib/iter_selection.py
+++ b/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/Memory.pulldown/lib/iter_selection.py
@@ -1,9 +1,9 @@
 # -*- coding: utf-8 -*-
-#pylint: disable=import-error,invalid-name,broad-except,superfluous-parens
 import os.path as op
 import pickle
+from System import Int64
 
-from pyrevit import revit, DB
+from pyrevit import revit, DB, HOST_APP
 from pyrevit import script
 
 
@@ -12,18 +12,16 @@
 
 def iterate(mode, step_size=1):
     """Iterate over elements in memorized selection"""
-    index_datafile = \
-        script.get_document_data_file("SelListPrevNextIndex", "pym")
-    datafile = \
-        script.get_document_data_file("SelList", "pym")
+    index_datafile = script.get_document_data_file("SelListPrevNextIndex", "pym")
+    datafile = script.get_document_data_file("SelList", "pym")
 
     selection = revit.get_selection()
 
     if op.exists(index_datafile):
-        with open(index_datafile, 'rb') as f:
+        with open(index_datafile, "rb") as f:
             idx = pickle.load(f)
 
-        if mode == '-':
+        if mode == "-":
             idx = idx - step_size
         else:
             idx = idx + step_size
@@ -32,7 +30,7 @@ def iterate(mode, step_size=1):
 
     if op.exists(datafile):
         try:
-            with open(datafile, 'rb') as df:
+            with open(datafile, "rb") as df:
                 cursel = pickle.load(df)
 
             if cursel:
@@ -40,12 +38,12 @@ def iterate(mode, step_size=1):
                     idx = abs(idx / len(cursel)) * len(cursel) + idx
                 elif idx >= len(cursel):
                     idx = idx - abs(idx / len(cursel)) * len(cursel)
+                if HOST_APP.is_newer_than(2025):
+                    selection.set_to([DB.ElementId(Int64(list(cursel)[idx]))])
+                else:
+                    selection.set_to([DB.ElementId(int(list(cursel)[idx]))])
 
-                selection.set_to([DB.ElementId(int(list(cursel)[idx]))])
-
-                with open(index_datafile, 'wb') as f:
+                with open(index_datafile, "wb") as f:
                     pickle.dump(idx, f)
         except Exception as io_err:
-            logger.error(
-                'Error read/write to: %s | %s', datafile, io_err
-                )
+            logger.error("Error read/write to: %s | %s", datafile, io_err)
diff --git a/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/lib/copypastestate/actions.py b/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/lib/copypastestate/actions.py
index 7ff8ce9a96..b483b52a93 100644
--- a/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/lib/copypastestate/actions.py
+++ b/extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/lib/copypastestate/actions.py
@@ -1,9 +1,11 @@
 """Copy/Paste State Actions"""
-#pylint: disable=import-error,invalid-name,broad-except,superfluous-parens
+
+# pylint: disable=import-error,invalid-name,broad-except,superfluous-parens
 import math
+from System import Int64
 
 from pyrevit import PyRevitException
-from pyrevit import revit, DB
+from pyrevit import revit, DB, HOST_APP
 from pyrevit.framework import List
 from pyrevit.coreutils import logger
 from pyrevit.coreutils import moduleutils
@@ -41,7 +43,7 @@ def dir_orient(self):
 
 @moduleutils.mark(basetypes.COPYPASTE_MARKER_PROPNAME)
 class ViewZoomPanStateAction(basetypes.CopyPasteStateAction):
-    name = 'View Zoom/Pan State'
+    name = "View Zoom/Pan State"
     invalid_context_msg = "Type of active view is not supported"
     this_project = False
 
@@ -60,15 +62,16 @@ def copy(self):
             data=ViewZoomPanStateData(
                 view_type=active_view.ViewType,
                 corner_pts=active_ui_view.GetZoomCorners(),
-                dir_orient=dir_orient
+                dir_orient=dir_orient,
             ),
-            this_project=False
+            this_project=False,
         )
 
     def paste(self):
         # load data
-        vzps_data = script.load_data(slot_name=self.__class__.__name__,
-                                     this_project=False)
+        vzps_data = script.load_data(
+            slot_name=self.__class__.__name__, this_project=False
+        )
 
         view_type = vzps_data.view_type
         vc1, vc2 = vzps_data.corner_pts
@@ -77,32 +80,28 @@ def paste(self):
         active_ui_view = revit.uidoc.GetOpenUIViews()[0]
         if not self.is_compatible_viewtype(revit.active_view, view_type):
             raise PyRevitException(
-                'Saved view type (%s) is different from active view (%s)'
-                % (vzps_data.view_type, type(revit.active_view).ViewType))
+                "Saved view type (%s) is different from active view (%s)"
+                % (vzps_data.view_type, type(revit.active_view).ViewType)
+            )
         # load ViewOrientation3D
         if isinstance(revit.active_view, DB.View3D):
             if revit.active_view.IsLocked:
-                raise PyRevitException('Current view orientation is locked')
+                raise PyRevitException("Current view orientation is locked")
             revit.active_view.SetOrientation(dir_orient)
         elif isinstance(revit.active_view, DB.ViewSection):
             angle = dir_orient.AngleTo(revit.active_view.ViewDirection)
-            if not pyutils.almost_equal(angle, math.pi) \
-                    and not pyutils.almost_equal(angle, 0):
+            if not pyutils.almost_equal(angle, math.pi) and not pyutils.almost_equal(
+                angle, 0
+            ):
                 raise PyRevitException("Views are not parallel")
         active_ui_view.ZoomAndCenterRectangle(vc1, vc2)
 
     @staticmethod
     def validate_context():
-        return revit.get_selection().is_empty \
-            and isinstance(
-                revit.active_view,
-                (
-                    DB.ViewPlan,
-                    DB.ViewSection,
-                    DB.View3D,
-                    DB.ViewSheet,
-                    DB.ViewDrafting
-                ))
+        return revit.get_selection().is_empty and isinstance(
+            revit.active_view,
+            (DB.ViewPlan, DB.ViewSection, DB.View3D, DB.ViewSheet, DB.ViewDrafting),
+        )
 
 
 # =============================================================================
@@ -124,9 +123,8 @@ def view_orientation(self):
 
 @moduleutils.mark(basetypes.COPYPASTE_MARKER_PROPNAME)
 class SectionBox3DStateAction(basetypes.CopyPasteStateAction):
-    name = '3D Section Box State'
-    invalid_context_msg = \
-        "You must be on a 3D view to copy and paste 3D section box."
+    name = "3D Section Box State"
+    invalid_context_msg = "You must be on a 3D view to copy and paste 3D section box."
     this_project = False
 
     def copy(self):
@@ -136,18 +134,18 @@ def copy(self):
         script.store_data(
             slot_name=self.__class__.__name__,
             data=SectionBox3DStateData(
-                section_box=section_box,
-                view_orientation=view_orientation
+                section_box=section_box, view_orientation=view_orientation
             ),
-            this_project=False
+            this_project=False,
         )
 
     def paste(self):
-        sb3d_data = script.load_data(slot_name=self.__class__.__name__,
-                                     this_project=False)
+        sb3d_data = script.load_data(
+            slot_name=self.__class__.__name__, this_project=False
+        )
 
         active_ui_view = revit.uidoc.GetOpenUIViews()[0]
-        with revit.Transaction('Paste Section Box Settings'):
+        with revit.Transaction("Paste Section Box Settings"):
             revit.active_view.SetSectionBox(sb3d_data.section_box)
             revit.active_view.SetOrientation(sb3d_data.view_orientation)
         active_ui_view.ZoomToFit()
@@ -171,7 +169,7 @@ def source_viewid(self):
 
 @moduleutils.mark(basetypes.COPYPASTE_MARKER_PROPNAME)
 class VisibilityGraphicsAction(basetypes.CopyPasteStateAction):
-    name = 'Visibility Graphics'
+    name = "Visibility Graphics"
     invalid_context_msg = "View does not support Visibility Graphics settings"
 
     def copy(self):
@@ -179,20 +177,21 @@ def copy(self):
             slot_name=self.__class__.__name__,
             data=VisibilityGraphicsData(
                 source_viewid=revit.active_view.Id,
-            )
+            ),
         )
 
     def paste(self):
         vg_data = script.load_data(slot_name=self.__class__.__name__)
-        with revit.Transaction('Paste Visibility Graphics'):
+        with revit.Transaction("Paste Visibility Graphics"):
             revit.active_view.ApplyViewTemplateParameters(
-                revit.doc.GetElement(vg_data.source_viewid))
+                revit.doc.GetElement(vg_data.source_viewid)
+            )
 
     @staticmethod
     def validate_context():
-        return revit.get_selection().is_empty \
-            and not isinstance(revit.active_view,
-                               (DB.ViewSheet, DB.ViewSchedule))
+        return revit.get_selection().is_empty and not isinstance(
+            revit.active_view, (DB.ViewSheet, DB.ViewSchedule)
+        )
 
 
 # =============================================================================
@@ -220,9 +219,8 @@ def is_active(self):
 # TODO: correct so split crop regions can be copied as well
 @moduleutils.mark(basetypes.COPYPASTE_MARKER_PROPNAME)
 class CropRegionAction(basetypes.CopyPasteStateAction):
-    name = 'Crop Region'
-    invalid_context_msg = \
-        "Activate a view or select at least one cropable viewport"
+    name = "Crop Region"
+    invalid_context_msg = "Activate a view or select at least one cropable viewport"
     this_project = False
 
     @staticmethod
@@ -258,20 +256,19 @@ def copy(self):
                 data=CropRegionData(
                     cropregion_curveloop=cropregion_curve_loop,
                     crop_bbox=crop_bbox,
-                    is_active=view.CropBoxActive
+                    is_active=view.CropBoxActive,
                 ),
-                this_project=False
+                this_project=False,
             )
         else:
-            raise PyRevitException(
-                'Cannot read crop region of selected view'
-                )
+            raise PyRevitException("Cannot read crop region of selected view")
 
     def paste(self):
-        cr_data = script.load_data(slot_name=self.__class__.__name__,
-                                   this_project=False)
+        cr_data = script.load_data(
+            slot_name=self.__class__.__name__, this_project=False
+        )
         crv_loop = cr_data.cropregion_curveloop
-        with revit.Transaction('Paste Crop Region'):
+        with revit.Transaction("Paste Crop Region"):
             for view in CropRegionAction.get_cropable_views():
                 if isinstance(view, DB.View3D):
                     if cr_data.crop_bbox:
@@ -289,19 +286,19 @@ def validate_context():
 # =============================================================================
 
 
-ALIGNMENT_CROPBOX = 'Align to CROP BOX CORNER on Paste'
-ALIGNMENT_BASEPOINT = 'Align to BASE POINT on Paste'
+ALIGNMENT_CROPBOX = "Align to CROP BOX CORNER on Paste"
+ALIGNMENT_BASEPOINT = "Align to BASE POINT on Paste"
 
 ALIGNMENT_OPTIONS_COPY = [
     ALIGNMENT_CROPBOX,
     ALIGNMENT_BASEPOINT,
-    ]
+]
 
-ALIGNMENT_CENTER = 'Align to CENTER of Copied Crop Box'
-ALIGNMENT_TOP_LEFT = 'Align to TOP LEFT Corner of Copied Crop Box'
-ALIGNMENT_TOP_RIGHT = 'Align to TOP RIGHT Corner of Copied Crop Box'
-ALIGNMENT_BOTTOM_LEFT = 'Align to BOTTOM LEFT Corner of Copied Crop Box'
-ALIGNMENT_BOTTOM_RIGHT = 'Align to BOTTOM RIGHT Corner of Copied Crop Box'
+ALIGNMENT_CENTER = "Align to CENTER of Copied Crop Box"
+ALIGNMENT_TOP_LEFT = "Align to TOP LEFT Corner of Copied Crop Box"
+ALIGNMENT_TOP_RIGHT = "Align to TOP RIGHT Corner of Copied Crop Box"
+ALIGNMENT_BOTTOM_LEFT = "Align to BOTTOM LEFT Corner of Copied Crop Box"
+ALIGNMENT_BOTTOM_RIGHT = "Align to BOTTOM RIGHT Corner of Copied Crop Box"
 
 ALIGNMENT_OPTIONS = [
     ALIGNMENT_CENTER,
@@ -309,7 +306,7 @@ def validate_context():
     ALIGNMENT_TOP_RIGHT,
     ALIGNMENT_BOTTOM_LEFT,
     ALIGNMENT_BOTTOM_RIGHT,
-    ]
+]
 
 
 class ViewportPlacementData(object):
@@ -333,7 +330,7 @@ def offset_uv(self):
 
 @moduleutils.mark(basetypes.COPYPASTE_MARKER_PROPNAME)
 class ViewportPlacementAction(basetypes.CopyPasteStateAction):
-    name = 'Viewport Placement on Sheet'
+    name = "Viewport Placement on Sheet"
     invalid_context_msg = "At least one viewport must be selected"
     this_project = False
 
@@ -378,9 +375,9 @@ def isolate_axis(viewport, new_center, mode=None):
         Returns:
             DB.XYZ: modified point
         """
-        if mode in ('X', 'Y'):
+        if mode in ("X", "Y"):
             current_center = viewport.GetBoxCenter()
-            if mode == 'X':
+            if mode == "X":
                 return DB.XYZ(new_center.X, current_center.Y, 0)
             else:
                 return DB.XYZ(current_center.X, new_center.Y, 0)
@@ -398,10 +395,14 @@ def hide_all_elements(view):
             list[DB.ElementId]: list of hidden element ids
         """
         # hide all elements in the view
-        cl_view_elements = DB.FilteredElementCollector(view.Document, view.Id) \
-            .WhereElementIsNotElementType()
-        elements_to_hide = [el.Id for el in cl_view_elements.ToElements()
-                            if not el.IsHidden(view) and el.CanBeHidden(view)]
+        cl_view_elements = DB.FilteredElementCollector(
+            view.Document, view.Id
+        ).WhereElementIsNotElementType()
+        elements_to_hide = [
+            el.Id
+            for el in cl_view_elements.ToElements()
+            if not el.IsHidden(view) and el.CanBeHidden(view)
+        ]
         if elements_to_hide:
             try:
                 view.HideElements(List[DB.ElementId](elements_to_hide))
@@ -434,7 +435,8 @@ def activate_cropbox(view):
                                and "Annotation Crop Active"
         """
         cboxannoparam = view.get_Parameter(
-            DB.BuiltInParameter.VIEWER_ANNOTATION_CROP_ACTIVE)
+            DB.BuiltInParameter.VIEWER_ANNOTATION_CROP_ACTIVE
+        )
         current_active = view.CropBoxActive
         current_visible = view.CropBoxVisible
         current_annotations = cboxannoparam.AsInteger()
@@ -456,7 +458,8 @@ def recover_cropbox(view, saved_values):
         """
         saved_active, saved_visible, saved_annotations = saved_values
         cboxannoparam = view.get_Parameter(
-            DB.BuiltInParameter.VIEWER_ANNOTATION_CROP_ACTIVE)
+            DB.BuiltInParameter.VIEWER_ANNOTATION_CROP_ACTIVE
+        )
         view.CropBoxActive = saved_active
         view.CropBoxVisible = saved_visible
         if not cboxannoparam.IsReadOnly:
@@ -474,9 +477,11 @@ def get_title_block_placement(viewport):
             DB.XYZ: TitleBlock location or DB.XYZ.Zero
         """
         sheet = viewport.Document.GetElement(viewport.SheetId)
-        cl = DB.FilteredElementCollector(sheet.Document, sheet.Id). \
-            WhereElementIsNotElementType(). \
-            OfCategory(DB.BuiltInCategory.OST_TitleBlocks)
+        cl = (
+            DB.FilteredElementCollector(sheet.Document, sheet.Id)
+            .WhereElementIsNotElementType()
+            .OfCategory(DB.BuiltInCategory.OST_TitleBlocks)
+        )
         title_blocks = cl.ToElements()
         if len(title_blocks) == 1:
             return title_blocks[0].Location.Point
@@ -507,13 +512,11 @@ def zero_cropbox(view):
         p2 = revit.units.project_to_world(uv2, view)
         p4 = revit.units.project_to_world(uv4, view)
         p1 = revit.units.project_to_world(
-            revit.units.project_to_viewport(p1, view),
-            view
-            )
+            revit.units.project_to_viewport(p1, view), view
+        )
         p3 = revit.units.project_to_world(
-            revit.units.project_to_viewport(p3, view),
-            view
-            )
+            revit.units.project_to_viewport(p3, view), view
+        )
         # create lines between points
         l1 = DB.Line.CreateBound(p1, p2)
         l2 = DB.Line.CreateBound(p2, p3)
@@ -528,29 +531,27 @@ def zero_cropbox(view):
     def copy(self):
         viewports = revit.get_selection().include(DB.Viewport)
         if len(viewports) != 1:
-            raise Exception('Exactly one viewport must be selected')
+            raise PyRevitException("Exactly one viewport must be selected")
         viewport = viewports[0]
         view = revit.doc.GetElement(viewport.ViewId)
 
-        title_block_pt = \
-            ViewportPlacementAction.get_title_block_placement(viewport)
+        title_block_pt = ViewportPlacementAction.get_title_block_placement(viewport)
 
         if view.ViewType in [DB.ViewType.DraftingView, DB.ViewType.Legend]:
             alignment = ALIGNMENT_CROPBOX
         else:
             alignment = forms.CommandSwitchWindow.show(
-                ALIGNMENT_OPTIONS_COPY,
-                message='Select Alignment Option')
+                ALIGNMENT_OPTIONS_COPY, message="Select Alignment Option"
+            )
 
         if not alignment:
             return
 
-        with revit.DryTransaction('Activate & Read Cropbox, Copy Center'):
+        with revit.DryTransaction("Activate & Read Cropbox, Copy Center"):
             if alignment == ALIGNMENT_BASEPOINT:
                 revit.update.set_crop_region(
-                    view,
-                    ViewportPlacementAction.zero_cropbox(view)
-                    )
+                    view, ViewportPlacementAction.zero_cropbox(view)
+                )
             # use cropbox as alignment if it active
             if alignment == ALIGNMENT_BASEPOINT or view.CropBoxActive:
                 ViewportPlacementAction.activate_cropbox(view)
@@ -567,83 +568,83 @@ def copy(self):
             data=ViewportPlacementData(
                 alignment=alignment,
                 center=center,
-                offset_uv=offset_uv if alignment == ALIGNMENT_CROPBOX else None
+                offset_uv=offset_uv if alignment == ALIGNMENT_CROPBOX else None,
             ),
-            this_project=False
+            this_project=False,
         )
 
     def paste(self):
-        vp_data = script.load_data(slot_name=self.__class__.__name__,
-                                   this_project=False)
+        vp_data = script.load_data(
+            slot_name=self.__class__.__name__, this_project=False
+        )
 
         viewports = revit.get_selection().include(DB.Viewport)
         align_axis = None
         if __shiftclick__:  # pylint: disable=undefined-variable
             align_axis = forms.CommandSwitchWindow.show(
-                ['X', 'Y', 'XY'],
-                message='Align specific axis?')
+                ["X", "Y", "XY"], message="Align specific axis?"
+            )
         # read saved values
         alignment = vp_data.alignment
         center = vp_data.center
         offset_uv = vp_data.offset_uv
         if alignment == ALIGNMENT_CROPBOX:
             corner_alignment = forms.CommandSwitchWindow.show(
-                ALIGNMENT_OPTIONS,
-                message='Select Alignment Option'
-                )
+                ALIGNMENT_OPTIONS, message="Select Alignment Option"
+            )
 
-        with revit.TransactionGroup('Paste Viewport Location'):
+        with revit.TransactionGroup("Paste Viewport Location"):
             for viewport in viewports:
                 view = revit.doc.GetElement(viewport.ViewId)
-                title_block_pt = \
-                    ViewportPlacementAction.get_title_block_placement(viewport)
+                title_block_pt = ViewportPlacementAction.get_title_block_placement(
+                    viewport
+                )
                 crop_region_current = None
                 cropbox_values_current = None
                 hidden_elements = None
                 # set temporary settings: hide elements, set cropbox visibility
                 #  if cropbox is not active, do nothing (use visible elements)
                 if alignment == ALIGNMENT_BASEPOINT or view.CropBoxActive:
-                    with revit.Transaction('Temporary settings'):
+                    with revit.Transaction("Temporary settings"):
                         # 'base point' mode - set cropbox to 'zero' temporary
                         if alignment == ALIGNMENT_BASEPOINT:
                             crop_region_current = revit.query.get_crop_region(view)
                             revit.update.set_crop_region(
                                 view, ViewportPlacementAction.zero_cropbox(view)
-                                )
-                        cropbox_values_current = \
+                            )
+                        cropbox_values_current = (
                             ViewportPlacementAction.activate_cropbox(view)
-                        hidden_elements = \
-                            ViewportPlacementAction.hide_all_elements(view)
+                        )
+                        hidden_elements = ViewportPlacementAction.hide_all_elements(
+                            view
+                        )
 
-                with revit.Transaction('Apply Viewport Placement'):
+                with revit.Transaction("Apply Viewport Placement"):
                     new_center = center
                     # 'crop box' mode - align to vp corner if necessary
-                    if alignment == ALIGNMENT_CROPBOX \
-                            and corner_alignment:
+                    if alignment == ALIGNMENT_CROPBOX and corner_alignment:
                         offset_xyz = ViewportPlacementAction.calculate_offset(
-                            view, offset_uv, corner_alignment)
+                            view, offset_uv, corner_alignment
+                        )
                         new_center += offset_xyz
                     # isolate alignment by x- or y-axis if necessary
                     new_center = ViewportPlacementAction.isolate_axis(
-                        viewport, new_center, align_axis)
+                        viewport, new_center, align_axis
+                    )
                     # apply new viewport position
                     viewport.SetBoxCenter(new_center + title_block_pt)
                 # unset temporary values
                 if crop_region_current:
-                    with revit.Transaction('Recover crop region form'):
+                    with revit.Transaction("Recover crop region form"):
                         revit.update.set_crop_region(view, crop_region_current)
                 if cropbox_values_current:
-                    with revit.Transaction('Recover crop region values'):
+                    with revit.Transaction("Recover crop region values"):
                         ViewportPlacementAction.recover_cropbox(
-                            view,
-                            cropbox_values_current
-                            )
+                            view, cropbox_values_current
+                        )
                 if hidden_elements:
-                    with revit.Transaction('Recover hidden elements'):
-                        ViewportPlacementAction.unhide_elements(
-                            view,
-                            hidden_elements
-                            )
+                    with revit.Transaction("Recover hidden elements"):
+                        ViewportPlacementAction.unhide_elements(view, hidden_elements)
 
     @staticmethod
     def validate_context():
@@ -669,7 +670,7 @@ def filter_ids(self):
 
 @moduleutils.mark(basetypes.COPYPASTE_MARKER_PROPNAME)
 class FilterOverridesAction(basetypes.CopyPasteStateAction):
-    name = 'Filter Overrides'
+    name = "Filter Overrides"
     invalid_context_msg = ""
 
     @staticmethod
@@ -677,18 +678,23 @@ def get_suitable_views():
         selected_views = revit.get_selection().only_views()
         if not selected_views:
             selected_views = [revit.uidoc.ActiveGraphicalView]
-        return [view for view in selected_views
-                if view.AreGraphicsOverridesAllowed()]
+        return [view for view in selected_views if view.AreGraphicsOverridesAllowed()]
 
     @staticmethod
     def controlled_by_template(view):
         if view.ViewTemplateId != DB.ElementId.InvalidElementId:
             view_template = view.Document.GetElement(view.ViewTemplateId)
-            non_controlled_params = view_template \
-                .GetNonControlledTemplateParameterIds()
+            non_controlled_params = view_template.GetNonControlledTemplateParameterIds()
             # check if filters are controlled by template
-            if DB.ElementId(int(DB.BuiltInParameter.VIS_GRAPHICS_FILTERS)) \
-                    not in non_controlled_params:
+            if HOST_APP.is_newer_than(2025):
+                filter_vis_graphics_parameter_id = DB.ElementId(
+                    Int64(DB.BuiltInParameter.VIS_GRAPHICS_FILTERS)
+                )
+            else:
+                filter_vis_graphics_parameter_id = DB.ElementId(
+                    int(DB.BuiltInParameter.VIS_GRAPHICS_FILTERS)
+                )
+            if filter_vis_graphics_parameter_id not in non_controlled_params:
                 return True
         return False
 
@@ -698,25 +704,24 @@ def copy(self):
             view = views[0]
             view_filters = revit.query.get_view_filters(view)
             if not view_filters:
-                raise PyRevitException('Active/Selected view has no fitlers applied')
+                raise PyRevitException("Active/Selected view has no fitlers applied")
 
             selected_filters = forms.SelectFromList.show(
                 view_filters,
-                name_attr='Name',
-                title='Select filters to copy',
-                button_name='Select filters',
-                multiselect=True
+                name_attr="Name",
+                title="Select filters to copy",
+                button_name="Select filters",
+                multiselect=True,
             )
 
             if not selected_filters:
-                raise PyRevitException('No filters selected. Cancelled.')
+                raise PyRevitException("No filters selected. Cancelled.")
 
             script.store_data(
                 slot_name=self.__class__.__name__,
                 data=FilterOverridesData(
-                    source_viewid=view.Id,
-                    filter_ids=[f.Id for f in view_filters]
-                )
+                    source_viewid=view.Id, filter_ids=[f.Id for f in view_filters]
+                ),
             )
 
     def paste(self):
@@ -726,35 +731,38 @@ def paste(self):
         source_filter_ids = fo_data.filter_ids
 
         # to view template or to selected view
-        mode_templates = \
+        mode_templates = (
             forms.CommandSwitchWindow.show(
-                ['Active/Selected View', 'Select View Templates'],
-                message='Where do you want to paste filters?'
-                ) == 'Select View Templates'
+                ["Active/Selected View", "Select View Templates"],
+                message="Where do you want to paste filters?",
+            )
+            == "Select View Templates"
+        )
         if mode_templates:
             views = forms.select_viewtemplates()
         else:
             views = FilterOverridesAction.get_suitable_views()
             views = [view for view in views if view.Id != source_view.Id]
-            views_controlled_by_template = \
-                [x for x in views
-                 if FilterOverridesAction.controlled_by_template(x)]
+            views_controlled_by_template = [
+                x for x in views if FilterOverridesAction.controlled_by_template(x)
+            ]
             if views_controlled_by_template:
                 forms.alert(
-                    'You have selected views with template applied.'
-                    ' They will be skipped'
-                    )
+                    "You have selected views with template applied."
+                    " They will be skipped"
+                )
         if not views:
-            raise PyRevitException('No suitable views selected or active')
+            raise PyRevitException("No suitable views selected or active")
 
         # check if there are views controlled by template
-        with revit.TransactionGroup('Paste Filter Overrides'):
+        with revit.TransactionGroup("Paste Filter Overrides"):
             for view in views:
-                with revit.Transaction('Paste filter'):
+                with revit.Transaction("Paste filter"):
                     # check if filters are controlled by template
                     if FilterOverridesAction.controlled_by_template(view):
-                        mlogger.warn('Skip view \'{}\''.format(
-                            revit.query.get_name(view)))
+                        mlogger.warn(
+                            "Skip view '{}'".format(revit.query.get_name(view))
+                        )
                         continue
 
                     view_filters_ids = view.GetFilters()
@@ -763,8 +771,7 @@ def paste(self):
                         if filter_id in view_filters_ids:
                             view.RemoveFilter(filter_id)
                         # add new fitler override
-                        filter_overrides = source_view.GetFilterOverrides(
-                            filter_id)
+                        filter_overrides = source_view.GetFilterOverrides(filter_id)
                         view.SetFilterOverrides(filter_id, filter_overrides)
 
     @staticmethod


Reviews:
user: login: devloai[bot]
body: ### PR Summary:
This PR addresses compatibility with Revit 2026's new Int64 ElementId type. The change adds a version check in the MRead script to use `System.Int64` for element ID conversion in Revit 2026+, fixing an issue where the selection restore feature wasn't working in newer versions of Revit.

### Review Summary:
The implementation resolves the compatibility issue correctly by checking the Revit version and using the appropriate conversion type. I've suggested using the existing compatibility helper function for consistency with MWrite and better maintainability. I've also noted an opportunity to improve error handling for a better user experience.

Feel free to provide feedback on this review which I'll take into account for future reviews.

#### Follow-up suggestions:
@devloai implement the suggested compatibility helper function approach
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T08:04:00Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR adds support for Revit 2026’s 64-bit `ElementId` by importing `System.Int64` and wrapping ID values when running on newer hosts. It also refactors string quoting and trailing commas for consistency.

- Introduce `Int64` and `HOST_APP.is_newer_than(2025)` checks to construct `DB.ElementId` correctly.
- Apply consistent double-quote style and trailing commas in multi-argument calls.
- Update various scripts and actions to conditionally cast element IDs to `Int64`.

### Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

<details>
<summary>Show a summary per file</summary>

| File                                                                                  | Description                                                      |
| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| copypastestate/actions.py                                                             | Add `Int64`, use `HOST_APP` to wrap element IDs, style cleanup  |
| iter_selection.py                                                                     | Import `Int64`, conditionally wrap element IDs, logging cleanup |
| Save Memory as Selection.pushbutton/script.py                                         | Import `Int64`, wrap IDs when creating `SelectionFilterElement` |
| MRead.pushbutton/script.py                                                            | Import `Int64`, wrap IDs when restoring selection               |
| Create Schedule from CSV.pushbutton/script.py                                         | Import `Int64`, wrap CSV-parsed IDs when setting parameters     |
</details>



<details>
<summary>Comments suppressed due to low confidence (3)</summary>

**extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/lib/iter_selection.py:40**
* Using `/` produces floats, which can make `idx` non-integer and break indexing. Replace `/` with integer division `//` or explicitly cast to `int`.
```
idx = idx - abs(idx / len(cursel)) * len(cursel)
```
**extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/lib/copypastestate/actions.py:764**
* Undefined name `mlogger` will cause a runtime error. Replace with the imported `logger` or define `mlogger`.
```
mlogger.warn("Skip view '{}'".format(revit.query.get_name(view)))
```
**extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/lib/iter_selection.py:48**
* The `logger` symbol is not defined in this module, causing a `NameError`. Import or define a logger (e.g., `logger = script.get_logger()`) or use an existing logging facility.
```
logger.error("Error read/write to: %s | %s", datafile, io_err)
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T08:57:36Z

Review comments:
diff_hunk: @@ -1,23 +1,25 @@
 import pickle
 
-from pyrevit.framework import List
 from pyrevit import script
-from pyrevit import revit, DB
-
+from pyrevit import revit, DB, HOST_APP
+from System import Int64
 
 selection = revit.get_selection()
 
 datafile = script.get_document_data_file("SelList", "pym")
 
-
 try:
-    f = open(datafile, 'rb')
+    f = open(datafile, "rb")
     current_selection = pickle.load(f)
     f.close()
 
     element_ids = []
-    for elid in current_selection:
-        element_ids.append(DB.ElementId(int(elid)))
+    if HOST_APP.is_newer_than(2025):
path: extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/mema.stack/MRead.pushbutton/script.py
user: login: devloai[bot]
body: Consider utilizing the `get_elementid_value_func()` helper from the `pyrevit.compat` module as it's already used in the `MWrite.pushbutton/script.py` file. This would make the code more consistent with the writing operation and easier to maintain across different Revit versions.
created_at: 2025-05-23T08:04:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,23 +1,25 @@
 import pickle
 
-from pyrevit.framework import List
 from pyrevit import script
-from pyrevit import revit, DB
-
+from pyrevit import revit, DB, HOST_APP
+from System import Int64
 
 selection = revit.get_selection()
 
 datafile = script.get_document_data_file("SelList", "pym")
 
-
 try:
-    f = open(datafile, 'rb')
+    f = open(datafile, "rb")
     current_selection = pickle.load(f)
     f.close()
 
     element_ids = []
-    for elid in current_selection:
-        element_ids.append(DB.ElementId(int(elid)))
+    if HOST_APP.is_newer_than(2025):
+        for elid in current_selection:
+            element_ids.append(DB.ElementId(Int64(elid)))
+    else:
+        for elid in current_selection:
+            element_ids.append(DB.ElementId(int(elid)))
 
     selection.set_to(element_ids)
 except Exception:
path: extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/mema.stack/MRead.pushbutton/script.py
user: login: devloai[bot]
body: The empty exception handler here is hiding any potential errors without providing feedback to the user. Consider logging the exception or at least providing a more specific exception type to catch. A user-friendly error message would help with troubleshooting if the selection restore fails.
created_at: 2025-05-23T08:04:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -528,29 +531,27 @@ def zero_cropbox(view):
     def copy(self):
         viewports = revit.get_selection().include(DB.Viewport)
         if len(viewports) != 1:
-            raise Exception('Exactly one viewport must be selected')
+            raise Exception("Exactly one viewport must be selected")
path: extensions/pyRevitTools.extension/pyRevit.tab/Selection.panel/memo.stack/lib/copypastestate/actions.py
user: login: Copilot
body: [nitpick] Raising a bare `Exception` is not consistent with other errors (`PyRevitException`). Consider using `PyRevitException` for clearer intent and uniform error handling.
```suggestion
            raise PyRevitException("Exactly one viewport must be selected")
```
created_at: 2025-05-23T08:57:35Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-23T09:16:46Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25143+0904-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15206467087)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-26T06:11:50Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25146+0559-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15247293122)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-29T10:25:16Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25149+1012-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15321450451)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-29T19:56:46Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25149+1943-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15332029404)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-02T15:12:46Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25153+1459-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15395558670)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-03T09:08:34Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25154+0858-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15412944322)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-03T09:30:44Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25154+0920-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15413442991)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-04T09:21:33Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25155+0904-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15437610302)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-11T11:39:45Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25162+1125-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15583493178)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-11T13:16:33Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25162+1306-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15585617158)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-11T20:40:49Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25162+2030-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15594951406)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-13T07:10:13Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25164+0700-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15628454288)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-13T13:57:46Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25164+1347-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15636046294)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-13T14:35:27Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25164+1420-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15636716356)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-13T18:44:25Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25164+1830-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15641415665)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-20T08:09:05Z
body: :package: New work-in-progress (wip) builds are available for [5.1.0.25171+0757-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15774108075)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-30T13:25:44Z
body: :package: New work-in-progress (wip) builds are available for [5.2.0.25181+1313-wip](https://github.com/pyrevitlabs/pyRevit/actions/runs/15973807401)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-06-30T13:44:37Z
body: :package: New public release are available for [5.2.0.25181+1425](https://github.com/pyrevitlabs/pyRevit/releases/tag/untagged-40ff17e053559dc898b3)
author_association: CONTRIBUTOR