Rocket U2 | UniVerse & UniData

Expand all | Collapse all

U2Py Error Handling

  • 1.  U2Py Error Handling

    Posted 04-08-2021 11:52
    I am building an ODM/ORM for my system in Python. With the intent of making my queries and writes into UV more pythonic and easier to code (I am VERY lazy). For me this is primarily allowing read and writes to and from UV with a Python dictionary leveraging the readnamedfields() and writenamedfield() within the u2py.File class. While I have a specific issue below, I think there is room for a general discussion around u2Py error handling.

    My specific issue lies in getting "u2py.U2Error: (3) unknown " back in my trace. I am getting this error back when trying to write data to a record I have open in editor (so the file is locked). While I would expect to get an error here, I would also expect that error to be a little more descriptive of what the issue is.

    I have a couple questions that should hopefully give me a good path forward here...

    1. Are there plans to improve the u2Py module's error handling in the near term future?
    2. Does the (3) in my error mean/represent anything? (I could parse this to get what I need if it does)
    3. has anyone built their own error class within the U2Py module to resolve issues like this? If so, I'd love to hear about it.

    My plan is to use the u2py.File's islocked() method to catch the error and make a check and raise a more specific error, (u2py.FileLockedError or something) but if it has already been done, I hate to reinvent the wheel.

    I'm going to mark this an "Open ended Discussion" post in the hopes that a discussion can arise surrounding U2Py Errors. Admins, my feelings will not be hurt if this post is changed to a question instead.

    ------------------------------
    Nic Davidson
    Developer
    Combined Transport
    OR United States
    ------------------------------


  • 2.  RE: U2Py Error Handling

    Posted 04-09-2021 12:40
    Nic,

    1. I do not believe there are plans to enhance the the u2py error handling in the near future.

    2. Not sure how you got the error 3,  when trying to write something that is locked, I would expect something like:

    U2Error: (30002) File or record is locked by another user

    How is the file opened, outside of the Python program?
    Can you put a small snip it of code?

    3. I have a few examples of functions I wrote to do the reads and writes, yet none with extensive lock handling at this time.

    I do agree this topic should stay open.  Note that the topic of an ORM has come up in the past, I have tinkered with writing my own from time to time.  I have a few functions at the Rocket Github site, that I use as a poor man's ORM, I tend to use it more than my more elaborate attempts ( which in most cases uses these functions ).

    In the u2support.py module, there is u2PyReadDict and u2PyWriteDict, that allows you to use a list of U2 Dictionary Items, when reading from the U2 file, it returns a Python Dictionary, that you can use/update and write back.

    i.e.
    python> import u2support
    python> u2dictitems = [ "FIRST_NAME", "LAST_NAME", "CITY", "STATE", "ZIP" ]
    python> pydict = u2support.u2ReadPyDict( "MEMBERS", "0110", u2dictitems )

    Welcome to the XDEMO Account
    Version: 3.1.5

    python> pydict
    {'FIRST_NAME': 'Anita', 'LAST_NAME': 'Johnson', 'CITY': 'New Glasgow', 'STATE': 'Nebraska', 'ZIP': '01870'}
    python>

    Note this sample code is not that extensive, and does not handle locking at this time.


    ------------------------------
    Michael Rajkowski
    Rocket Software
    ------------------------------



  • 3.  RE: U2Py Error Handling

    Posted 04-09-2021 14:02
    Hey Michael, I appreciate the info. Let me get some code snippets in here. I do intend to throw the ODM on GitHub once I have it a little more refined. It turned into quite a large module when I started adding some of the ICONV and OCONV functionality, and to be frank....I would be embarrassed to show it all in it's current state :)

    I'm likely getting away from my initial topic, but this module is one of the cooler things I have done in a while, and I like showing off!

    The module itself is currently 4 classes, a U2Handler, U2Reader, and U2Writer.  My handler holds and works all of the U2py.File objects and acts as the wrapper around most of the u2py.File functionality. It gets the fieldnames (Dictionary record IDs) for the file. It also converts pythonic fieldnames (engine_make) to and from the Universe fieldnames (ENGINE.MAKE). So I can call a field with either, and the string is converted to a DynArray without me having to deal with it.

    UVReader and UVWriter use the u2py.File's read and write methods respectively. I get everything into one nice and clean object with a U2Odm class that inherits from U2Reader and U2Writer. Passing *args and **kwargs up as needed. I did all of this because I am lazy, and us Python guys have an irrational fear of caps lock :)

    I'll show you my read and write functions, as they are the heavy lifters, I expanded on my comments to hopefully help make understanding it easier, but if anyone has questions, let me know. Happy to post more.

    class U2Reader(U2Handler):
    
    .......
    
        def read_field(self, record_id, field_name):
            """ reads and returns a value of a field corresponding to the record_id and a field name. 
            Converts pythonic fieldnames (equip_status) to UV ones (EQUIP.STATUS)  by replaceing . with _ and lowercase. """
            if isinstance(record_id, list):
                record_id = record_id[0]
            # _py_to_uv changes a Pythonic field name into a UV fiendly one. 
            field_name = self._py_to_uv(field_name)
            # make my field name a DynArray so I can use it with U2py. 
            dyn_array_field = u2py.DynArray(field_name)
            # self.uv_file is a u2py.File object of the file I am working with. This is created in the U2Handler class on the __init__
            value = self.uv_file.readnamedfields(record_id, dyn_array_field, self.lock_flag)
            if field_name == "@ID":
                # if we are looking at the Record ID field, we need to just keep it as is. 
                rec_id = value.to_list()[0]
                return rec_id
            value = self._unarray(value)
            if self.value_convert:
                # convert the values, if we are supposed to. 
                #UV string to Py Object will use the Conversion code in the UVDict file to convert to a proper python object (list, dict, int...)
                value = self.uv_string_to_py_obj(value)
                conv_code = self.get_conversion_code(field_name)
                # if my value is None, why convert it? 
                if value == None:
                    return value
                else:
                    # if I dont have a conversion code in my UVDict, I can't convert it. 
                    if conv_code:
                        value = self.uv_string_conversion_code(conv_code, value)
                        return value
                    else:
                        return value
            else:
                return value​
    class U2Writer(U2Handler):
    
    
    .........
    
    
        def write_field(self, record_id, field_name, value):
            """ Writes to a named field. Converts pythonic objects to approppriate strings when necessary. """
            # change my pythonic fieldname into a UV one
            field = self._py_to_uv(field_name)
            if value == None:
                # If the value None, keep the writeable_value as none
                writeable_value = None
            else:
                if isinstance(value, datetime.datetime):
                    # If the value is a datetime object, it needs to be converted
                    # regardless of self.calue_convert. If u2py could handle datetime objects this would go away!
                    # I have a Python dict in a config file that maps UV CONV codes to the Datetime library partner. EX: DTIZ gets paired with "%Y-%m-%dT%H:%M:%SZ"
                    conv_code = self.get_conversion_code(field)
                    if conv_code:
                        writeable_value = self.apply_conversion_code(value, conv_code)
                    else: 
                        writeable_value = value
                elif self.value_convert and not isinstance(value, datetime.datetime):
                    if isinstance(value, str):
                        writeable_value = value
                    else:
                        conv_code = self.get_conversion_code(field)
                        writeable_value = self.apply_conversion_code(value, conv_code)
                else:
                    writeable_value = value
    
            dyn_field = u2py.DynArray(field)
            dyn_value = u2py.DynArray([writeable_value])
            if self.locking:
                # set exclusive lock - Locking logic is not done yet, placeholder
                self.lock_record(record_id, True)
            try:
                self.uv_file.writenamedfields(record_id, dyn_field, dyn_value)
            except u2py.U2Error as e:
                # printing here for debugging. I have not made my logger for this module yet,
                #  
                print(record_id, dyn_field, dyn_value)
                raise e
            if self.verify:
                # Not implemented as of yet, but will eventually veify writes and log errors as such. 
                self.verify_write(record_id, field, value)
            if self.uv_file.islocked(record_id):
                # If for some reason I locked the file, make sure I unlock it at the end. 
                self.unlock_record(record_id)
            return


    As you can see, I am basically just using readnamedfield() and writenamedfields() on the u2py.File object. The beauty for this is that my read/write code looks like:

    def odm_test():
    
      # Start up my U2Odm object with a file name. I can also pass paramaters on locking, write verificaition and a boolean on if I should try to convert fields based on line 3 of the field's UVdictonary. 
      my_odm = u2odm.U2Odm('equipment_sales')
      # Instead of having to type "ENG.MAIN.MAKE" I can keep my variables pythonic. Us younger python guys are allergic to capital letters...
      value = my_odm.read_field(3257, 'eng_main_make')
      write_val = my_odm.write_field('3257', 'eng_main_make', value)​

    When I run this with the file open in an editor (ED EQUIPMENT.SALES 3257) I get the following:

    U2 ERROR
    3257 ENG.MAIN.MAKE 857
    Traceback (most recent call last):
      File "PP/nictest.py", line 197, in <module>
        odm_test()
      File "PP/nictest.py", line 190, in odm_test
        write_val = my_odm.write_field('3257', 'eng_main_make', value)
      File "/u2/COMBINED/GM.JR/PP/u2odm/odm.py", line 412, in write_field
        raise e
      File "/u2/COMBINED/GM.JR/PP/u2odm/odm.py", line 408, in write_field
        self.uv_file.writenamedfields(record_id, dyn_field, dyn_value)
    u2py.U2Error: (3) unknown​
    # The "(3) unknown" is the weirdest thing here. I do get a proper 3001 code for record id not found when I break that intentionally. 

    My intent is to make my ODM(ORM?) more robust and catch/try to resolve these errors. My read_as_dict(), read_as_list(), write_dict() and write_list() methods all call the above functions to make their magic happen.

    Because we are smart programmers, here is a min working example just using u2py

    def nic_breaks_u2py():
    
      my_file = u2py.File('EQUIPMENT.SALES')
      field_arr = u2py.DynArray('ENG.MAIN.MAKE')
      value = my_file.readnamedfields('3257', field_arr)
      write = my_file.writenamedfields('3257', field_arr, value)

    This will work if the record is not open, but will show the same (3) unknown error if the file is open in an editor.

    ------------------------------
    Nic Davidson
    Developer
    Combined Transport
    OR United States
    ------------------------------



  • 4.  RE: U2Py Error Handling

    Posted 04-09-2021 19:00
    Nic,

    Interesting, I am not getting an error 3, but it does raise an exception.

    Note I altered your example to use the XDEMO MEMBERS file

    python> my_file = u2py.File('MEMBERS')
    python> field_arr = u2py.DynArray([ "@ID", "FIRST_NAME" ] )
    python> value = my_file.readnamedfields('0110', field_arr)
    python> try:
    ... my_write = my_file.writenamedfields('0110', field_arr, value)
    ... except u2py.U2Error as e:
    ... print( "the item can not be locked")
    ... print( str(e))
    ...
    the item can not be locked
    (305727) unknown
    python>

    Yet, if I get a lock prior to trying to write the data, it returns the message that it is locked.


    python> my_file = u2py.File('MEMBERS')
    python> field_arr = u2py.DynArray([ "@ID", "FIRST_NAME" ] )
    python> value = my_file.readnamedfields('0110', field_arr)
    python> try:
    ... my_file.lock("0110")
    ... my_write = my_file.writenamedfields('0110', field_arr, value)
    ... except u2py.U2Error as e:
    ... print( "the item can not be locked")
    ... print( str(e))
    ...
    the item can not be locked
    (30002) File or record is locked by another user

    Can I get the Database Version and the OS you are running this on?

    ------------------------------
    Michael Rajkowski
    Rocket Software
    ------------------------------



  • 5.  RE: U2Py Error Handling

    Posted 30 days ago
    DB Version is 11.3.2.7001
    On RHEL 7.7

    I thought your idea about trying explicitly locking the file in the try loop was a good one so I tried it myself...and the error is displaying how I would expect it to.  I am thinking I am doing something wrong with the locking in my ORM. I am going to target that area and see what I can find. If I run a test without any type of locking, I am getting the unknown error, from my uneducated perspective I am guessing Python is not understanding the lock flag when the record is being locked within the TCL editor, OR my ORM is not actually locking the record appropriately.

    Would a general best practice/recommendation be to do a shared lock on read operations and an exclusive lock on write operations?


    ------------------------------
    Nic Davidson
    Developer
    Combined Transport
    OR United States
    ------------------------------



  • 6.  RE: U2Py Error Handling

    Posted 04-09-2021 19:22
    Hi guys,
    I use UniObjects for .NET for a while and noted (in all versions) that if READU a new item I get error number 30001. That's ok.
    Then release the new item and try to READU the same again, i get no error number. That's wrong, must get 30001 again.
    I don't know if both issues have the same root. I use UniObjects for .NET version 3.171.1.9003 and I don't know if is the latest.

    El vie, 9 abr 2021 a las 15:02, Nic Davidson via Rocket Forum (<Mail@forum.rocketsoftware.com>) escribió:
    Hey Michael, I appreciate the info. Let me get some code snippets in here. I do intend to throw the ODM on GitHub once I have it a little more... -posted to the "Rocket U2 | UniVerse & UniData" forum
    Be sure to join the forums you're interested in to be notified of new content. Click the join button from either the forum listing page or the home page of any given sub-forum.

    Tip: Want a single update on all your forum memberships? Go to Profile > My Account > Forum Notifications, and check 'daily consolidated digest.' Switch the discussion email drop down to 'no email' or you will receive both.
    Rocket Software

    Rocket U2 | UniVerse & UniData

    Post New Message Online
    Re: U2Py Error Handling
    Reply to Group Online Reply to Group
    Nic Davidson
    Apr 9, 2021 2:02 PM
    Nic Davidson
    Hey Michael, I appreciate the info. Let me get some code snippets in here. I do intend to throw the ODM on GitHub once I have it a little more refined. It turned into quite a large module when I started adding some of the ICONV and OCONV functionality, and to be frank....I would be embarrassed to show it all in it's current state :)

    I'm likely getting away from my initial topic, but this module is one of the cooler things I have done in a while, and I like showing off!

    The module itself is currently 4 classes, a U2Handler, U2Reader, and U2Writer.  My handler holds and works all of the U2py.File objects and acts as the wrapper around most of the u2py.File functionality. It gets the fieldnames (Dictionary record IDs) for the file. It also converts pythonic fieldnames (engine_make) to and from the Universe fieldnames (ENGINE.MAKE). So I can call a field with either, and the string is converted to a DynArray without me having to deal with it.

    UVReader and UVWriter use the u2py.File's read and write methods respectively. I get everything into one nice and clean object with a U2Odm class that inherits from U2Reader and U2Writer. Passing *args and **kwargs up as needed. I did all of this because I am lazy, and us Python guys have an irrational fear of caps lock :)

    I'll show you my read and write functions, as they are the heavy lifters, I expanded on my comments to hopefully help make understanding it easier, but if anyone has questions, let me know. Happy to post more.

    class U2Reader(U2Handler): ....... def read_field(self, record_id, field_name): """ reads and returns a value of a field corresponding to the record_id and a field name. Converts pythonic fieldnames (equip_status) to UV ones (EQUIP.STATUS) by replaceing . with _ and lowercase. """ if isinstance(record_id, list): record_id = record_id[0] # _py_to_uv changes a Pythonic field name into a UV fiendly one. field_name = self._py_to_uv(field_name) # make my field name a DynArray so I can use it with U2py. dyn_array_field = u2py.DynArray(field_name) # self.uv_file is a u2py.File object of the file I am working with. This is created in the U2Handler class on the __init__ value = self.uv_file.readnamedfields(record_id, dyn_array_field, self.lock_flag) if field_name == "@ID": # if we are looking at the Record ID field, we need to just keep it as is. rec_id = value.to_list()[0] return rec_id value = self._unarray(value) if self.value_convert: # convert the values, if we are supposed to. #UV string to Py Object will use the Conversion code in the UVDict file to convert to a proper python object (list, dict, int...) value = self.uv_string_to_py_obj(value) conv_code = self.get_conversion_code(field_name) # if my value is None, why convert it? if value == None: return value else: # if I dont have a conversion code in my UVDict, I can't convert it. if conv_code: value = self.uv_string_conversion_code(conv_code, value) return value else: return value else: return value class U2Writer(U2Handler): ......... def write_field(self, record_id, field_name, value): """ Writes to a named field. Converts pythonic objects to approppriate strings when necessary. """ # change my pythonic fieldname into a UV one field = self._py_to_uv(field_name) if value == None: # If the value None, keep the writeable_value as none writeable_value = None else: if isinstance(value, datetime.datetime): # If the value is a datetime object, it needs to be converted # regardless of self.calue_convert. If u2py could handle datetime objects this would go away! # I have a Python dict in a config file that maps UV CONV codes to the Datetime library partner. EX: DTIZ gets paired with "%Y-%m-%dT%H:%M:%SZ" conv_code = self.get_conversion_code(field) if conv_code: writeable_value = self.apply_conversion_code(value, conv_code) else: writeable_value = value elif self.value_convert and not isinstance(value, datetime.datetime): if isinstance(value, str): writeable_value = value else: conv_code = self.get_conversion_code(field) writeable_value = self.apply_conversion_code(value, conv_code) else: writeable_value = value dyn_field = u2py.DynArray(field) dyn_value = u2py.DynArray([writeable_value]) if self.locking: # set exclusive lock - Locking logic is not done yet, placeholder self.lock_record(record_id, True) try: self.uv_file.writenamedfields(record_id, dyn_field, dyn_value) except u2py.U2Error as e: # printing here for debugging. I have not made my logger for this module yet, # print(record_id, dyn_field, dyn_value) raise e if self.verify: # Not implemented as of yet, but will eventually veify writes and log errors as such. self.verify_write(record_id, field, value) if self.uv_file.islocked(record_id): # If for some reason I locked the file, make sure I unlock it at the end. self.unlock_record(record_id) return

    As you can see, I am basically just using readnamedfield() and writenamedfields() on the u2py.File object. The beauty for this is that my read/write code looks like:

    def odm_test(): # Start up my U2Odm object with a file name. I can also pass paramaters on locking, write verificaition and a boolean on if I should try to convert fields based on line 3 of the field's UVdictonary. my_odm = u2odm.U2Odm('equipment_sales') # Instead of having to type "ENG.MAIN.MAKE" I can keep my variables pythonic. Us younger python guys are allergic to capital letters... value = my_odm.read_field(3257, 'eng_main_make') write_val = my_odm.write_field('3257', 'eng_main_make', value)
    When I run this with the file open in an editor (ED EQUIPMENT.SALES 3257) I get the following:

    U2 ERROR 3257 ENG.MAIN.MAKE 857 Traceback (most recent call last): File "PP/nictest.py", line 197, in <module> odm_test() File "PP/nictest.py", line 190, in odm_test write_val = my_odm.write_field('3257', 'eng_main_make', value) File "/u2/COMBINED/GM.JR/PP/u2odm/odm.py", line 412, in write_field raise e File "/u2/COMBINED/GM.JR/PP/u2odm/odm.py", line 408, in write_field self.uv_file.writenamedfields(record_id, dyn_field, dyn_value) u2py.U2Error: (3) unknown # The "(3) unknown" is the weirdest thing here. I do get a proper 3001 code for record id not found when I break that intentionally.
    My intent is to make my ODM(ORM?) more robust and catch/try to resolve these errors. My read_as_dict(), read_as_list(), write_dict() and write_list() methods all call the above functions to make their magic happen.

    Because we are smart programmers, here is a min working example just using u2py

    def nic_breaks_u2py(): my_file = u2py.File('EQUIPMENT.SALES') field_arr = u2py.DynArray('ENG.MAIN.MAKE') value = my_file.readnamedfields('3257', field_arr) write = my_file.writenamedfields('3257', field_arr, value)
    This will work if the record is not open, but will show the same (3) unknown error if the file is open in an editor.

    ------------------------------
    Nic Davidson
    Developer
    Combined Transport
    OR United States
    ------------------------------
      Reply to Group Online   View Thread   Recommend   Forward   Flag as Inappropriate   Post New Message Online  




     
    You are subscribed to "Rocket U2 | UniVerse & UniData" as perinsl@gmail.com. To change your subscriptions, go to My Subscriptions. To unsubscribe from this community discussion, go to Unsubscribe.


    --
     Ing. Sergio Perin
    Baseware Systems



    Original Message:
    Sent: 4/9/2021 2:02:00 PM
    From: Nic Davidson
    Subject: RE: U2Py Error Handling

    Hey Michael, I appreciate the info. Let me get some code snippets in here. I do intend to throw the ODM on GitHub once I have it a little more refined. It turned into quite a large module when I started adding some of the ICONV and OCONV functionality, and to be frank....I would be embarrassed to show it all in it's current state :)

    I'm likely getting away from my initial topic, but this module is one of the cooler things I have done in a while, and I like showing off!

    The module itself is currently 4 classes, a U2Handler, U2Reader, and U2Writer.  My handler holds and works all of the U2py.File objects and acts as the wrapper around most of the u2py.File functionality. It gets the fieldnames (Dictionary record IDs) for the file. It also converts pythonic fieldnames (engine_make) to and from the Universe fieldnames (ENGINE.MAKE). So I can call a field with either, and the string is converted to a DynArray without me having to deal with it.

    UVReader and UVWriter use the u2py.File's read and write methods respectively. I get everything into one nice and clean object with a U2Odm class that inherits from U2Reader and U2Writer. Passing *args and **kwargs up as needed. I did all of this because I am lazy, and us Python guys have an irrational fear of caps lock :)

    I'll show you my read and write functions, as they are the heavy lifters, I expanded on my comments to hopefully help make understanding it easier, but if anyone has questions, let me know. Happy to post more.

    class U2Reader(U2Handler):
    
    .......
    
        def read_field(self, record_id, field_name):
            """ reads and returns a value of a field corresponding to the record_id and a field name. 
            Converts pythonic fieldnames (equip_status) to UV ones (EQUIP.STATUS)  by replaceing . with _ and lowercase. """
            if isinstance(record_id, list):
                record_id = record_id[0]
            # _py_to_uv changes a Pythonic field name into a UV fiendly one. 
            field_name = self._py_to_uv(field_name)
            # make my field name a DynArray so I can use it with U2py. 
            dyn_array_field = u2py.DynArray(field_name)
            # self.uv_file is a u2py.File object of the file I am working with. This is created in the U2Handler class on the __init__
            value = self.uv_file.readnamedfields(record_id, dyn_array_field, self.lock_flag)
            if field_name == "@ID":
                # if we are looking at the Record ID field, we need to just keep it as is. 
                rec_id = value.to_list()[0]
                return rec_id
            value = self._unarray(value)
            if self.value_convert:
                # convert the values, if we are supposed to. 
                #UV string to Py Object will use the Conversion code in the UVDict file to convert to a proper python object (list, dict, int...)
                value = self.uv_string_to_py_obj(value)
                conv_code = self.get_conversion_code(field_name)
                # if my value is None, why convert it? 
                if value == None:
                    return value
                else:
                    # if I dont have a conversion code in my UVDict, I can't convert it. 
                    if conv_code:
                        value = self.uv_string_conversion_code(conv_code, value)
                        return value
                    else:
                        return value
            else:
                return value​
    class U2Writer(U2Handler):
    
    
    .........
    
    
        def write_field(self, record_id, field_name, value):
            """ Writes to a named field. Converts pythonic objects to approppriate strings when necessary. """
            # change my pythonic fieldname into a UV one
            field = self._py_to_uv(field_name)
            if value == None:
                # If the value None, keep the writeable_value as none
                writeable_value = None
            else:
                if isinstance(value, datetime.datetime):
                    # If the value is a datetime object, it needs to be converted
                    # regardless of self.calue_convert. If u2py could handle datetime objects this would go away!
                    # I have a Python dict in a config file that maps UV CONV codes to the Datetime library partner. EX: DTIZ gets paired with "%Y-%m-%dT%H:%M:%SZ"
                    conv_code = self.get_conversion_code(field)
                    if conv_code:
                        writeable_value = self.apply_conversion_code(value, conv_code)
                    else: 
                        writeable_value = value
                elif self.value_convert and not isinstance(value, datetime.datetime):
                    if isinstance(value, str):
                        writeable_value = value
                    else:
                        conv_code = self.get_conversion_code(field)
                        writeable_value = self.apply_conversion_code(value, conv_code)
                else:
                    writeable_value = value
    
            dyn_field = u2py.DynArray(field)
            dyn_value = u2py.DynArray([writeable_value])
            if self.locking:
                # set exclusive lock - Locking logic is not done yet, placeholder
                self.lock_record(record_id, True)
            try:
                self.uv_file.writenamedfields(record_id, dyn_field, dyn_value)
            except u2py.U2Error as e:
                # printing here for debugging. I have not made my logger for this module yet,
                #  
                print(record_id, dyn_field, dyn_value)
                raise e
            if self.verify:
                # Not implemented as of yet, but will eventually veify writes and log errors as such. 
                self.verify_write(record_id, field, value)
            if self.uv_file.islocked(record_id):
                # If for some reason I locked the file, make sure I unlock it at the end. 
                self.unlock_record(record_id)
            return


    As you can see, I am basically just using readnamedfield() and writenamedfields() on the u2py.File object. The beauty for this is that my read/write code looks like:

    def odm_test():
    
      # Start up my U2Odm object with a file name. I can also pass paramaters on locking, write verificaition and a boolean on if I should try to convert fields based on line 3 of the field's UVdictonary. 
      my_odm = u2odm.U2Odm('equipment_sales')
      # Instead of having to type "ENG.MAIN.MAKE" I can keep my variables pythonic. Us younger python guys are allergic to capital letters...
      value = my_odm.read_field(3257, 'eng_main_make')
      write_val = my_odm.write_field('3257', 'eng_main_make', value)​

    When I run this with the file open in an editor (ED EQUIPMENT.SALES 3257) I get the following:

    U2 ERROR
    3257 ENG.MAIN.MAKE 857
    Traceback (most recent call last):
      File "PP/nictest.py", line 197, in <module>
        odm_test()
      File "PP/nictest.py", line 190, in odm_test
        write_val = my_odm.write_field('3257', 'eng_main_make', value)
      File "/u2/COMBINED/GM.JR/PP/u2odm/odm.py", line 412, in write_field
        raise e
      File "/u2/COMBINED/GM.JR/PP/u2odm/odm.py", line 408, in write_field
        self.uv_file.writenamedfields(record_id, dyn_field, dyn_value)
    u2py.U2Error: (3) unknown​
    # The "(3) unknown" is the weirdest thing here. I do get a proper 3001 code for record id not found when I break that intentionally. 

    My intent is to make my ODM(ORM?) more robust and catch/try to resolve these errors. My read_as_dict(), read_as_list(), write_dict() and write_list() methods all call the above functions to make their magic happen.

    Because we are smart programmers, here is a min working example just using u2py

    def nic_breaks_u2py():
    
      my_file = u2py.File('EQUIPMENT.SALES')
      field_arr = u2py.DynArray('ENG.MAIN.MAKE')
      value = my_file.readnamedfields('3257', field_arr)
      write = my_file.writenamedfields('3257', field_arr, value)

    This will work if the record is not open, but will show the same (3) unknown error if the file is open in an editor.

    ------------------------------
    Nic Davidson
    Developer
    Combined Transport
    OR United States
    ------------------------------

    Original Message:
    Sent: 04-09-2021 12:40
    From: Michael Rajkowski
    Subject: U2Py Error Handling

    Nic,

    1. I do not believe there are plans to enhance the the u2py error handling in the near future.

    2. Not sure how you got the error 3,  when trying to write something that is locked, I would expect something like:

    U2Error: (30002) File or record is locked by another user

    How is the file opened, outside of the Python program?
    Can you put a small snip it of code?

    3. I have a few examples of functions I wrote to do the reads and writes, yet none with extensive lock handling at this time.

    I do agree this topic should stay open.  Note that the topic of an ORM has come up in the past, I have tinkered with writing my own from time to time.  I have a few functions at the Rocket Github site, that I use as a poor man's ORM, I tend to use it more than my more elaborate attempts ( which in most cases uses these functions ).

    In the u2support.py module, there is u2PyReadDict and u2PyWriteDict, that allows you to use a list of U2 Dictionary Items, when reading from the U2 file, it returns a Python Dictionary, that you can use/update and write back.

    i.e.
    python> import u2support
    python> u2dictitems = [ "FIRST_NAME", "LAST_NAME", "CITY", "STATE", "ZIP" ]
    python> pydict = u2support.u2ReadPyDict( "MEMBERS", "0110", u2dictitems )

    Welcome to the XDEMO Account
    Version: 3.1.5

    python> pydict
    {'FIRST_NAME': 'Anita', 'LAST_NAME': 'Johnson', 'CITY': 'New Glasgow', 'STATE': 'Nebraska', 'ZIP': '01870'}
    python>

    Note this sample code is not that extensive, and does not handle locking at this time.


    ------------------------------
    Michael Rajkowski
    Rocket Software