Python count

Is there any (fairly easy) way to find out roughly how many lines of Python code you’ve got in your Dynamo script? :snake:

Don’t think so. Why do you need to know that?

As a vague explanation for my boss as to why this current project has run massively over time… :expressionless:

Copy/paster to notepad or VS code.

Tried that… but it shows python nodes then all the code on one line.

To prevent corruption and data loss when opening, all ‘in graph code’ is serialized into a flat string. So:

  1. Open the .DYN using a json library. The resulting object is a dictionary containing the segments of data which make up a .dyn.
  2. Get the nodes list from the dictionary. Each item in this list is a dictionary, containing the info about each node.
  3. For each item in the nodes list, if the node type is “PythonScriptNode” then get the “Code” value from the dictionary; otherwise skip it. The code is a flat string with escape characters used to ensure symbols like new lines, or quotation marks don’t cause the code to be misread when you’re opening it in Dynamo.
  4. For each code item, count the occurrences of \r\n in the string.
  5. Sum the list of counts.

You may want to discount commented lines, and doubled returns (or in some cases quintupled returns), and stuff like multi line strings (""" as the demarcation).

If there is desire for insight on this from multiple people I can likely put something together quickly.

4 Likes

I think this will be useful for a coming project that’s going to involve handing over a lot of DYN, so I went ahead and did this. Note that the json.load appears to be cleaning up the text as is, saving the need for searching for newline characters.

import os, json #import the os and json modules into the python environment
directory = IN[0] #the directory to get the data from 
walk = os.walk(directory)#get all the stuff in the directory
dynFiles = [] #an empty list for all the dyn files
for step in walk: #for every directory in the walked list 
    paths = [os.path.join(step[0],f) for f in step[2]] #build a path from the file names and directories in each step
    [dynFiles.append(f) for f in paths if f.endswith(".dyn")] #get the files which end with a .dyn
codes = "" #an empty string to hold the total sum of all code in the .dyns
for dyn in dynFiles: #for every .dyn in the list of dyn files
    with open(dyn,'r') as f: #open the file 
        txts = json.load(f) #load the json string
        nodes = txts["Nodes"] #get the nodes section of the dyn
        for node in nodes: #for every node in the nodes list 
            keys = node.keys() #get the keys from the node
            if "Code" in keys: #if the node has a "Code" key
                codes = codes+node["Code"]+"\r" #append the code to the nodes tring, with a trailing return for the next dyn
OUT = len(codes.splitlines()) #count the number of split lines in the combined code
3 Likes

A little variant


# Load the Python Standard and DesignScript Libraries
import sys
import clr
import json
import os
import re

path = IN[0]

dictresume = {"Total_Python_lines" : 0, "Total_Python_Nodes" : 0}
pythonNodes = 0
if path.endswith(".dyn"):
    with open(path, 'r', encoding='utf8') as f:
        data = json.loads(f.read())
        lst_dictNode = data.get("Nodes")
        for dictNode in lst_dictNode:
            if dictNode.get('NodeType') == 'PythonScriptNode':
                print("Python Node find")
                dictresume["Total_Python_Nodes"] += 1
                pythonscript = dictNode.get('Code')
                lst_line = re.findall(r'\r\n', pythonscript)
                dictresume["Total_Python_lines"] += len(lst_line)
            
OUT = dictresume
4 Likes

Ah! Should have noted that I changed my plan a bit on the fly to count lines of code in code blocks as well (which was more of my need anyway).

Great work as always @c.poupin!

1 Like