← Home About Archive Photos Replies indieweb Also on Micro.blog
  • stackoverflow.com/questions…

    stackoverflow.com/questions…

    → 11:17 AM, Sep 28
  • Static dispatch

    This tells you what to put in your python code:

    (hello-rsa-python) PS D:\dev\source\local\rsa\python\hello-rsa-python> py .\venv\Lib\site-packages\win32com\client\makepy.py -i
    Robot Object Model ver. 24.0
     {F3A37BD0-AA2D-11D2-9844-0080C86BE4DF}, lcid=0, major=1, minor=0
     >>> # Use these commands in Python code to auto generate .py support
     >>> from win32com.client import gencache
     >>> gencache.EnsureModule('{F3A37BD0-AA2D-11D2-9844-0080C86BE4DF}', 0, 1, 0)
    (hello-rsa-python) PS D:\dev\source\local\rsa\python\hello-rsa-python>
    

    This generates the python file for the entire library:

    (hello-rsa-python) PS D:\dev\source\local\rsa\python\hello-rsa-python\makepy> py ..\venv\Lib\site-packages\win32com\client\makepy.py "Robot Object Model ver. 24.0"
    Generating to C:\Users\johnsobr\AppData\Local\Temp\gen_py\3.11\F3A37BD0-AA2D-11D2-9844-0080C86BE4DFx0x1x0.py
    Building definitions from type library...
    Generating...
    Importing module
    (hello-rsa-python) PS D:\dev\source\local\rsa\python\hello-rsa-python>
    

    So, putting this in a python module and running it…

    from win32com.client import gencache
    
    gencache.EnsureModule("{F3A37BD0-AA2D-11D2-9844-0080C86BE4DF}", 0, 1, 0)
    

    creates the same library file in the same local temp directory as running the command line call:

    makepy.py "Robot Object Model ver. 24.0"
    

    The gencache.py has an EnsureModule() function and an EnsureDispatch(). The Dispatch variant seems to be a wrapper around EnsureModule(). I’m guessing there is a reason for this, but I fail to see it at this time nor understand when to use one over the other.

    → 9:21 PM, Sep 27
  • Hello World for the Autodesk Robot Structural Analysis Pro API using Python

    RSA Pro with python.

    Let’s start with pywin32. Install into a virtual environment:

    pip install pywin32
    

    The script:

    import win32com.client as win32
    
    rsa = win32.gencache.EnsureDispatch("Robot.Application")
    print("Hello, World")
    print(rsa.__module__)
    
    rsa.Visible = True
    
    _ = input("Press ENTER to quit:")
    
    rsa.Quit(0)
    

    The rundown:

    The API for Autodesk Robot Structural Analysis Professional is based on COM. Thus, for this example we import the win32com.client module and use win32.gencache.EnsureDispatch("Robot.Application") to create the application object. I forget the details at the moment, but there are two approaches to generating the Python code to wrap the COM object: dynamic and static. I think this is the dynamic approach as pywin32 created the IRobotApplication.py file in C:\Users\<user_name>\AppData\Local\Temp\gen_py\3.11\F3A37BD0-AA2D-11D2-9844-0080C86BE4DFx0x1x0. More to come on all this as I research it all again.

    The print(rsa.__module__) line outputs win32com.gen_py.F3A37BD0-AA2D-11D2-9844-0080C86BE4DFx0x1x0.IRobotApplication

    RSA Pro is launched, but not visible. rsa.Visible = True renders the application to the screen.

    The console may very well close automatically, so the _ = input("Press ENTER to quit:") allows us to pause and admire our work.

    Finally, calling rsa.Quit(0) closes the application. An IRobotQuitOption, despite its name, is required. The API documentation lists three options. The enums are self-explanatory:

    I_QO_DISCARD_CHANGES = 0
    I_QO_PROMPT_TO_SAVE_CHANGES = 1
    I_QO_SAVE_CHANGES = 2
    

    Resources:

    [https://pypi.org/project/pywin32/] [https://www.autodesk.com/support/technical/article/caas/sfdcarticles/sfdcarticles/Where-can-be-found-SDK-package-for-Robot-Structural-Analysis-2021.html]

    → 4:59 PM, Sep 27
  • This video helped me understand a behavior in tkinter to use a button to create a drawing on a tk.Canvas object: youtu.be/Il5ujscQ0…

    → 7:41 AM, Sep 27
  • Visual Scripting Ideas

    Would be great to have visual scripting provide a list of node options for an input based on the inputs signature. Example: Bending stress requires a moment and an elastic section modulus. A moment is characterized by a float/double, units of force-length (e.g. kip-feet). Options with that type of signature output would be functions like wL^2 or PL. Alternatively, it could be a direct input box or a list of moments.

    If a list is fed to a function as an input, then the function could use a map or similar “functional” functions. Perhaps there is a default and options to choose from.

    I also think ASTs would be good for transforming a graph into python code. A recent reference: youtu.be/XhWvz4dK4…

    → 7:40 AM, Sep 27
  • Installing PowerShell 7 using winget

    To Install PowerShell 7 using winget, first find the Id.

    PowerShell

    winget search Microsoft.PowerShell
    Name               Id                           Version Source
    ---------------------------------------------------------------
    PowerShell         Microsoft.PowerShell         7.3.6.0 winget
    PowerShell Preview Microsoft.PowerShell.Preview 7.4.0.5 winget
    

    Then install it using the noted Id.

    winget install --id Microsoft.Powershell --source winget
    
    → 4:02 PM, Sep 12
  • Using cURL in PowerShell

    How cURL on PowerShell with an Autodesk Platform Services authentication endpoint example. Put this in a script (<script_name>.ps1) or copy and paste directly to the prompt.

    $CLIENT_ID = '<Get from your App portal'
    $CLIENT_SECRET = '<Get from your App portal>'
    $CLIENT_CONVERT = $CLIENT_ID + ':' + $CLIENT_SECRET
    $CLIENT_CONVERT
    $creds = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($CLIENT_CONVERT))
    curl.exe -v 'https://developer.api.autodesk.com/authentication/v2/token' `
        -X 'POST' `
        -H 'Content-Type: application/x-www-form-urlencoded' `
        -H 'Accept: application/json' `
        -H "Authorization: Basic $creds" `
        -d 'grant_type=client_credentials' `
        -d 'scope=data:read'
    

    Note that curl is a PowerShell alias for the Invoke-WebRequest cmdlet. You need to invoke curl.exe for it to behave like the bash version.

    There are two approaches to use with Invoke-webRequest.

    The first uses command line flags.

    Invoke-WebRequest https://developer.api.autodesk.com/authentication/v2/token `
        -Headers @{Accept = 'application/json'; `
            Authorization = "Basic $creds" 
    } `
        -ContentType 'application/x-www-form-urlencoded' `
        -Method 'Post' `
        -Body @{grant_type = 'client_credentials'; `
            scope          = 'data:read'
    }
    

    The second encapsulates all the parameters into a dictionary.

    $LoginParameters = @{
        Uri     = 'https://developer.api.autodesk.com/authentication/v2/token'
        Method  = 'POST'
        Headers = @{
            Accept        = 'application/json'
            Authorization = "Basic $creds"
        }
        Content = 'application/x-www-form-urlencoded'
        Body    = @{grant_type = 'client_credentials'
            scope           = 'data:read'
        }
    }
    
    Invoke-WebRequest @LoginParameters
    

    Reference:

    • Invoke-WebRequest
    → 3:20 PM, Sep 12
  • How to encode a string to Base64 on a bash shell:

    $ echo -n 'my-string' | base64
    bXktc3RyaW5n
    

    On PowerShell:

    > [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('my-string'))
    bXktc3RyaW5n
    
    → 3:10 PM, Sep 12
  • String Concatenation in PowerShell

    I ran into an issue concatenating a string within the following PowerShell command.

    $ curl.exe -v 'https://developer.api.autodesk.com/authentication/v2/token' `
        -X 'POST' `
        -H 'Content-Type: application/x-www-form-urlencoded' `
        -H 'Accept: application/json' `
        -H "Authorization: Basic $creds" `
        -d 'grant_type=client_credentials' `
        -d 'scope=data:read'
    

    What worked in the built-in PowerShell 5.x and PowerShell 7.x:

    "Authorization: Basic $creds"
    

    This worked in PowerShell 7.x but not PowerShell 5.x:

    'Authorization: Basic ' + $creds
    

    Their output looks the same when you create a variable from each an echo them to the terminal, but the API server does not like the latter.

    Here is a good reference: https://lazyadmin.nl/powershell/concatenate-string/

    → 3:04 PM, Sep 12
  • Name resolution error on WSL2 Ubuntu

    I get the following error message using WSL2 Ubuntu:

    $ ping google.com
    ping: google.com: Temporary failure in name resolution
    

    To fix this do the following:

    $ sudo nano /etc/resolv.conf
    

    Change or add:

    nameserver 8.8.8.8
    

    You probably have to do this each time you start a session. I think there is a fix, but my company has a security policy that I cannot change locally. Maybe, I can run a script during the session opening to update this file.

    → 2:35 PM, Sep 12
  • My journey with Python and SQLite | Part 3

    I added the remaining CRUD statements in Part 2. In this post, I’ll DRY up the code used for executing and committing the SQL statements.

    Create a new function that accepts the database path and the SQL statement. Create a single unit of work by pulling in the connection and cursor creation and closure, statement execution, and results. As this evolves into an app or a library, I would rather open the connection, do some work, and close the connection rather than worry about leaving a connection open by accident.

    def query_db(db_path, sql):
        # Open and connection and create a cursor
        conn = sqlite3.connect(db_path)
        cur = conn.cursor()
    
        # Execute the query and fetch the results
        cur = conn.execute(sql)
        conn.commit()  # If there are no open transactions, it is a no-op
        res = cur.fetchall()  # If there are no records, returns an empty list []
    
        # Close the cursor and connection
        cur.close()
        conn.close()
    
        return res
    

    Not all of the SQL statements executed from Python need the conn.commit() code. Per the documentation[1], the commit() method is a “no-op” and does nothing when there are no open transactions. Additionally, the documentation[2][3] indicates that fectchone() returns None[2] and fetchall() returns an empty list [][3] if there are no records to fetch. Use fetchall() to cover all bases for now. In the future, I may add some conditional statements to change what is returned from the function. For now, it is clean, and that is good for me.

    Now execute the single function after each SQL statement and print the result if desired.

        # Read a record by id
        sql = "SELECT ROWID, name, comments FROM institutions WHERE ROWID = 1"
        res = query_db(db_path, sql)
        print(res)
    

    Here is the module.

    institution_03.py

    import sqlite3
    import sys
    from pathlib import Path
    
    
    def query_db(db_path, sql):
        # Open and connection and create a cursor
        conn = sqlite3.connect(db_path)
        cur = conn.cursor()
    
        # Execute the query and fetch the results
        cur = conn.execute(sql)
        conn.commit()  # If there are no open transactions, it is a no-op
        res = cur.fetchall()  # If there are no records, returns an empty list []
    
        # Close the cursor and connection
        cur.close()
        conn.close()
    
        return res
    
    
    def main():
        db_path = Path(".") / "home.db"
    
        # Create the table
        sql = "CREATE TABLE IF NOT EXISTS institutions (name, comments)"
        res = query_db(db_path, sql)
    
        # Create one record
        sql = """INSERT INTO institutions (name, comments)
                VALUES ('ABC Bank', 'A regional bank')"""
        res = query_db(db_path, sql)
    
        # Create many records
        sql = """INSERT INTO institutions (name, comments)
                VALUES ('Chase', 'International'), ('Discover', 'National')"""
        res = query_db(db_path, sql)
    
        # Read a record by id
        sql = "SELECT ROWID, name, comments FROM institutions WHERE ROWID = 1"
        res = query_db(db_path, sql)
        print(res)
    
        # Read all records
        sql = "SELECT ROWID, name, comments FROM institutions"
        res = query_db(db_path, sql)
        print(res)
    
        # Update a record by id
        sql = "UPDATE institutions SET name = 'My Bank' WHERE ROWID = 1"
        res = query_db(db_path, sql)
    
        # Update all records
        sql = "UPDATE institutions SET comments = 'Comments are stupid'"
        res = query_db(db_path, sql)
    
        # Delete one record from the table by id
        sql = "DELETE FROM institutions WHERE ROWID = 1"
        res = query_db(db_path, sql)
    
        # Delete all records from the table
        sql = "DELETE FROM institutions"
        res = query_db(db_path, sql)
    
        # Delete the table
        sql = "DROP TABLE IF EXISTS institutions"
        res = query_db(db_path, sql)
    
    
    if __name__ == "__main__":
        sys.exit(main())
    

    My journey with Python and SQLite

    • Part 1
    • Part 2
    • Part 3

    References:

    1. sqlite3 — DB-API 2.0 interface for SQLite databases, Python.org Documentation
    2. sqlite3 — DB-API 2.0 interface for SQLite databases | Cursor.fetchone, Python.org Documentation
    3. sqlite3 — DB-API 2.0 interface for SQLite databases | Cursor.fetchall, Python.org Documentation
    → 9:25 AM, Sep 11
  • My journey with Python and SQLite | Part 2

    In Part 1, I set up the database and created a table and one record in that table. In this post, I’ll fill out the rest of the CRUD statements for single records and multiple or all records. I use the SQL WHERE clause for single record requests to select by ROWID.

    I want to highlight that you use cur.fetchall() instead of cur.fetchone() to fetch all the records returned by a SELECT query.

        # Read all records
        sql = "SELECT ROWID, name, comments FROM institutions"
        cur = conn.execute(sql)
        res = cur.fetchall()  # <-- List of tuples
        print(res)
    

    Furthermore, the returned result is a list of tuples.

    [(1, 'ABC Bank', 'A regional bank'), (2, 'Chase', 'International'), (3, 'Discover', 'National')]
    

    The rest of the statements are self-explanatory, reflecting the SQL syntax and pattern to execute, commit, and fetch.

    Here is the updated module.

    institutions_02.py

    import sqlite3
    import sys
    from pathlib import Path
    
    
    def main():
        db_path = Path(".") / "home.db"
    
        # Open and connection and create a cursor
        conn = sqlite3.connect(db_path)
        cur = conn.cursor()
    
        # Create the table
        sql = "CREATE TABLE IF NOT EXISTS institutions (name, comments)"
        conn.execute(sql)
    
        # Create one record
        sql = """INSERT INTO institutions (name, comments)
                VALUES ('ABC Bank', 'A regional bank')"""
        conn.execute(sql)
        conn.commit()
    
        # Create many records
        sql = """INSERT INTO institutions (name, comments)
                VALUES ('Chase', 'International'), ('Discover', 'National')"""
        conn.execute(sql)
        conn.commit()
    
        # Read a record by id
        sql = "SELECT ROWID, name, comments FROM institutions WHERE ROWID = 1"
        cur = conn.execute(sql)
        res = cur.fetchone()  # <-- Tuple
        print(res)
    
        # Read all records
        sql = "SELECT ROWID, name, comments FROM institutions"
        cur = conn.execute(sql)
        res = cur.fetchall()  # <-- List of tuples
        print(res)
    
        # Update a record by id
        sql = "UPDATE institutions SET name = 'My Bank' WHERE ROWID = 1"
        cur = conn.execute(sql)
        conn.commit()
        print(res)
    
        # Update all records
        sql = "UPDATE institutions SET comments = 'Comments are stupid'"
        cur = conn.execute(sql)
        conn.commit()
    
        # Delete one record from the table by id
        sql = "DELETE FROM institutions WHERE ROWID = 1"
        conn.execute(sql)
        conn.commit()
    
        # Delete all records from the table
        sql = "DELETE FROM institutions"
        conn.execute(sql)
        conn.commit()
    
        # Delete the table
        sql = "DROP TABLE IF EXISTS institutions"
        conn.execute(sql)
    
        # Close the cursor and connection
        cur.close()
        conn.close()
    
    
    if __name__ == "__main__":
        sys.exit(main())
    

    My journey with Python and SQLite

    • Part 1
    • Part 2
    • Part 3
    → 8:41 PM, Sep 10
  • My journey with Python and SQLite | Part 1

    I want to create some apps for various personal projects. Thus, I’m learning how to use SQLite with Python[1]. Since I enjoy learning new things, I plan to start with SQL, which I learned many years ago and since forgot the specific query syntax. Yes, I know I could use SQLAlchemy. I used Ruby on Rails in the past, for which ActiveRecord is similar. However, I want to develop a good understanding of creating a CRUD workflow without all that abstraction.

    Here goes…starting with a single table and no related tables. I will use hard-coded SQL statements in this iteration to ensure I have the syntax correct and mitigate troubleshooting placeholder variables. Furthermore, I plan not to use explicit row ids and instead rely on SQLite’s ROWID alias[2]. I may change my mind on this in the future.

    During exploration, I’ll use SQLite’s command line tool. Python installs SQLite for us, but I’m unsure about the command line tools. I downloaded sqlite-tools-win32-x86-3430000.zip[3], unzipped and put the files in C:\Program Files (x86)\SQLite\, and added that directory to the end of Windows System variable, Path[4].

    Open the database with the SQLite shell to explore. Use .exit to exit the shell.

    sqlite3 .\home.db
    sqlite> .exit
    

    This first module needs three import statements. All are in Python’s standard library.

    import sqlite3
    import sys
    from pathlib import Path
    

    I don’t know if this will turn into a library module, but let’s use a main() function and if __name__ == "__main__": bit to keep things tidy and in good form[5]. Add a variable with the path to and name of the database. If no database exists, one will be created. Run with sys.exit(main()) instead of main() because reasons[6].

    def main():
        db_path = Path(".") / "home.db"
    
    
    if __name__ == "__main__":
        sys.exit(main())
    

    Open a connection to the database and create a cursor.

        conn = sqlite3.connect(db_path)
        cur = conn.cursor()
    

    Create a table called “institutions.” Execute the query.

        sql = "CREATE TABLE IF NOT EXISTS institutions (name, comments)"
        conn.execute(sql)
    

    I stop the execution of the module after this code to inspect the database. The first .schema shows that there are no tables in the database. The second shows that institutions was created.

    sqlite> .schema
    sqlite> .schema
    CREATE TABLE institutions (name, comments);
    

    Create a record in the institutions table.

        sql = """INSERT INTO institutions (name, comments)
                 VALUES ('ABC Bank', 'A regional bank')"""
        conn.execute(sql)
        conn.commit()
    

    An explicit commit statement is required to finish the transaction and write the data to the database.

    Let’s go to the command line and explore the database again. Yay, the record is there! Note that * will not fetch ROWID. Thus, ROWID is explicitly requested.

    sqlite> SELECT ROWID, * FROM institutions;
    1|ABC Bank|A regional bank
    

    Now, let’s fetch the record with Python. We need a cursor to fetch the record. In this case, use the cursor objected already created.

        sql = "SELECT ROWID, name, comments FROM institutions WHERE ROWID = 1"
        cur = conn.execute(sql)
        res = cur.fetchone()  # <-- Tuple
        print(res)
    

    The result from fetchone() is a tuple as shown with the print(res) statement.

    > py institution_01.py
    (1, 'ABC Bank', 'A regional bank')
    >
    

    Since I am exploring all this, I want to clean up the database. I probably should use pyTest for all of this, but it’s another thing to reason about. I just don’t feel like adding another layer to all this right now.

        sql = "DROP TABLE IF EXISTS institutions"
        conn.execute(sql)
    

    Let’s take a final look at the database. Yep, .schema returns nothing and the table is gone.

    sqlite> .schema
    sqlite>
    

    Do the right thing. Close the cursor[7] and the connection.

        cur.close()
        conn.close()
    

    Here is the complete module.

    institution_01.py

    import sqlite3
    import sys
    from pathlib import Path
    
    
    def main():
        db_path = Path(".") / "home.db"
    
        # Open and connection and create a cursor
        conn = sqlite3.connect(db_path)
        cur = conn.cursor()
    
        # Create the table
        sql = "CREATE TABLE IF NOT EXISTS institutions (name, comments)"
        conn.execute(sql)
    
        # Create one record
        sql = """INSERT INTO institutions (name, comments)
                 VALUES ('ABC Bank', 'A regional bank')"""
        conn.execute(sql)
        conn.commit()
    
        # Read a record by id
        sql = "SELECT ROWID, name, comments FROM institutions WHERE ROWID = 1"
        cur = conn.execute(sql)
        res = cur.fetchone()  # <-- Tuple
        print(res)
    
       # Delete the table
        sql = "DROP TABLE IF EXISTS institutions"
        conn.execute(sql)
    
        # Close the cursor and connection
        cur.close()
        conn.close()
    
    
    if __name__ == "__main__":
        sys.exit(main())
    

    My journey with Python and SQLite

    • Part 1
    • Part 2
    • Part 3

    References:

    1. sqlite3 — DB-API 2.0 interface for SQLite databases, Python.org Documentation
    2. SQLite Autoincrement, SQLite Documentation
    3. SQLite Download Page, SQLite Documentation
    4. How To Download & Install SQLite Tools, SQLite Tutorial
    5. __main__ — Top-level code environment, Python.org Documentation
    6. __main__ — Top-level code environment | Idiomatic usage, Python.org Documentation
    7. Solution: Always close the cursor for (even read-only) queries!, StackOverflow
    → 4:33 PM, Sep 10
  • RSS
  • JSON Feed
  • Micro.blog