Adafruit_ads1x15.ads1115 not comaptible with globals()[pkg_trunc] = importlib.import_module(pkg_trunc)

This issue was first discussed at: adafruit_ads1x15.ads1115 not comaptible with globals()[pkg_trunc] = importlib.import_module(pkg_trunc) · Issue #66 · adafruit/Adafruit_CircuitPython_ADS1x15 · GitHub, but has been closed as not an Adafruit issue.

My initial attempt at opeing a but at Issue 42928: adafruit_ads1x15.ads1115 not comaptible with globals()[pkg_trunc] = importlib.import_module(pkg_trunc) - Python tracker was rejected, with suggestion I try this forum.

The first attached script ada_dbg1.py.txt which uses simple hardcoded import statements, the creation of ads1115 objects work fine. The second attached script ada_dbg2.py.txt uses a loop to import a variable list of packages, which allows for more graceful error handling & messaging.

globals()[pkg_trunc] = importlib.import_module(pkg_trunc)

The high level observation is that board, busio & adafruit_extended_bus objects continue to be correctly created after the import loop is done. Its just adafruit_ads1x15.ads1115 that does not like this import method.

The same line of code creating ads10 gets error:
Traceback (most recent call last):
File “ada_dbg2.py”, line 33, in
ads10 = adafruit_ads1x15.ads1115.ADS1115(i2c_1, address=0x48)
AttributeError: module ‘adafruit_ads1x15’ has no attribute ‘ads1115’

When I look at the global variables, from ads_dbg1.py, I see:
‘adafruit_ads1x15’: <module ‘adafruit_ads1x15’ from ‘/home/pi/.local/lib/python3.7/site-packages/adafruit_ads1x15/init.py’>,

When I look at the global variables, from ads_dbg2.py, I see:
‘adafruit_ads1x15.ads1115’: <module ‘adafruit_ads1x15.ads1115’ from ‘/home/pi/.local/lib/python3.7/site-packages/adafruit_ads1x15/ads1115.py’>,

So I tried doing the import without the .ads1115 appended, & got:
‘adafruit_ads1x15’: <module ‘adafruit_ads1x15’ from ‘/home/pi/.local/lib/python3.7/site-packages/adafruit_ads1x15/init.py’>,

But I still get an error.
Traceback (most recent call last):
File “ada_dbg2.py”, line 33, in
ads10 = adafruit_ads1x15.ads1115.ADS1115(i2c_1, address=0x48)
AttributeError: module ‘adafruit_ads1x15’ has no attribute ‘ads1115’

I went through all 8 permutations of with/without the .ads1115 in the importlib.import_module statement, no luck.

Comments?

cleanup, reposted below

ada_dbg2.py script

import  importlib

pkg_list=["sys", "time", "traceback", "board", "busio",
          "adafruit_extended_bus", "adafruit_ads1x15.ads1115", "print_data"]
error_list = []
for pkg in pkg_list:
   pkg_trunc = pkg.split(".")[0]
   print("importing pkg: ", pkg, "pkg_trunc:", pkg_trunc)
   try:
      globals()[pkg_trunc] = importlib.import_module(name=pkg_trunc, package=pkg_trunc)
   except:
      error_list.append(pkg)
      (x,y,z)=sys.exc_info()
      print("ERROR: pkg:", pkg, "msg:", y)# y is root error msg
      # traceback.print_exc() # gives all info, more verbose

if len(error_list) == 0:
   print("All required packages loaded OK:", pkg_list)
else:
   print("ERROR: required package list:", pkg_list, " FAILED to load:", error_list)
   sys.exit(1)

print("\n\nglobals:", globals(),"\n\n")

# import adafruit_ads1x15.ads1115 as ADS
# from adafruit_ads1x15.analog_in import AnalogIn

i2c_1 = busio.I2C(board.SCL, board.SDA)
print_data.print_data(i2c_1)

# These lines use module rename as:
# from adafruit_extended_bus import ExtendedI2C as I2C
# i2c_4 = I2C(4) 
# Above can be rewritten without "as"
i2c_4 = adafruit_extended_bus.ExtendedI2C(4)
print_data.print_data(i2c_4)

# Create the ADC objects using two I2C bus
# print_data.print_data(adafruit_ads1x15.ads1115.ADS1115)
ads10 = adafruit_ads1x15.ads1115.ADS1115(i2c_1, address=0x48)
print_data.print_data(ads10)
ads13 = adafruit_ads1x15.ads1115.ADS1115(i2c_1, address=0x4b)
print_data.print_data(ads13)

# Create single-ended inputs on i2c-1 bus
# ch1_48_0 = AnalogIn(ads10, ADS.P0)



ads40 = adafruit_ads1x15.ads1115.ADS1115(i2c_4, address=0x48)
print_data.print_data(ads40)

ada_dbg1.py script

import sys, time, traceback, board, busio, \
   adafruit_extended_bus, adafruit_ads1x15.ads1115, print_data
print("\n\nglobals:", globals(),"\n\n")

# import adafruit_ads1x15.ads1115 as ADS
# from adafruit_ads1x15.analog_in import AnalogIn

i2c_1 = busio.I2C(board.SCL, board.SDA)
print_data.print_data(i2c_1)

# These lines use module rename as:
# from adafruit_extended_bus import ExtendedI2C as I2C
# i2c_4 = I2C(4) 
# Above can be rewritten without "as"
i2c_4 = adafruit_extended_bus.ExtendedI2C(4)
print_data.print_data(i2c_4)

# Create the ADC objects using two I2C bus
# print_data.print_data(adafruit_ads1x15.ads1115.ADS1115)
ads10 = adafruit_ads1x15.ads1115.ADS1115(i2c_1, address=0x48)
print_data.print_data(ads10)
ads13 = adafruit_ads1x15.ads1115.ADS1115(i2c_1, address=0x4b)
print_data.print_data(ads13)

# Create single-ended inputs on i2c-1 bus
# ch1_48_0 = AnalogIn(ads10, ADS.P0)

ads40 = adafruit_ads1x15.ads1115.ADS1115(i2c_4, address=0x48)
print_data.print_data(ads40)

print_data.py script - makes output of dir(object) easier for humans to read

import re ;# regular expressions need importing.

# Define version & author attributes for this package.
__version__ = '1.0'
__author__ = 'John Brearley <brearley@bell.net>'

# Prints an objects available methods, attribute names & values.
# NB: inspect.ismethod(item) always returns false.
# NB: Option verbose flag v defaults to False.
def print_data(obj,v=False):
   # Initialization
   methods="" ;# list of user defined methods
   skipped="" ;# list of items not shown
   vars="" ;# list of user variable=value pairs

   # If available, show object name in title display, instead of python raw hex name.
   print (" ")
   if (hasattr(obj,"name")):
      print("print_data obj:",getattr(obj,"name"),"class:",obj.__class__.__name__)
   else:
      print("print_data obj:",obj,"class:",obj.__class__.__name__)

   # Process all object data, including methods, attributes & variables
   item_list=dir(obj) 
   for item in item_list:
      # print("print_data checking:",item)
      # Skip the built in python items starting with "__"
      if (re.search("^__",item)):
         skipped=skipped+item+" "
         # print("print_data skipped: ",item," due to ^__")
         continue

      # Try to get value string of item.
      try:
         # getting attribute string value avoids some errors
         value=str(getattr(obj,item))
         # value=eval(obj.item) ;# doesnt work, with/without eval

         # Look at value for user defined methods vs variables
         if (re.search("^<(bound|built-in)",value)):
            methods=methods+item+" "
            # print("print_data method:",item+"="+value)
         else:
            vars=vars+item+"="+value+" "
            # print("print_data vars:",item+"="+value)
      except:
         skipped=skipped+item+" "
         print("print_data ERROR: skipped:",item," could not get value")

   # Display results
   print("print_data vars:",vars)
   print("print_data methods:",methods)
   # Show verbose info as optionally requested.
   if (v):
      try:
         print("print_data __dict__:",str(getattr(obj,"__dict__"))) ;# sometimes has all vars & values
      except:
         print("print_data: NO __dict__ !")
      print("print_data skipped:",skipped)
   print (" ")
   return

It’s not clear to me which libraries you’ve installed and how they’re installed, or what version of Python you’re using. The Adafruit_CircuitPython_ADS1x15 seem to require CircuitPython, which seems to need special hardware).

Hi Petr: Yes, this script runs on a Raspberry Pi, model 3B+ or higher. It makes use of the I2C bus that is part of the larger 40pin bus on the Raspberry Pi hardware. The I2C bus pins are connected to 3 of the ADS1115 analog-digitial converter boards.

My notes show I did:
pip3 install adafruit-circuitpython-ads1x15 # this loads multiple dependencies
pip3 install adafruit-extended-bus

Other related items, not necessarily used in these scripts:
sudo apt-get install i2c-tools
sudo apt-get install python3-smbus
sudo apt-get install python3-rpi.gpio

When I look at the list of Adafruit related libraries installed on the Raspberry Pi, I see:
pip3 list | grep -i -e “ada”
Adafruit-Blinka 5.9.2
adafruit-circuitpython-ads1x15 2.2.4
adafruit-circuitpython-busdevice 5.0.1
adafruit-circuitpython-mcp3xxx 1.4.2
adafruit-extended-bus 1.0.1
Adafruit-PlatformDetect 2.23.0
Adafruit-PureIO 1.1.8

I tried loading adafruit_ads1x15.analog_in via importlib.import_module and it works fine, see attached power_monitor_rpi.py.txt below.

When you run the script, the globals data shows:

'adafruit_extended_bus': <module 'adafruit_extended_bus' from '/home/pi/.local/lib/python3.7/site-packages/adafruit_extended_bus.py'>, 

'adafruit_ads1x15.analog_in': <module 'adafruit_ads1x15.analog_in' from '/home/pi/.local/lib/python3.7/site-packages/adafruit_ads1x15/analog_in.py'>, 

'adafruit_ads1x15': <module 'adafruit_ads1x15' from '/home/pi/.local/lib/python3.7/site-packages/adafruit_ads1x15/__init__.py'>}

The first two packages point to the lowest level .py file with code in them, but the adafruit_ads1x15 points to an (empty) --init–.py file.

I have asked the Adafruit people on github to take another look.

# script that runs on Raspberry Pi to monitor A-D converter inputs
# inputs to be monitore are defined in xxx_rc config file

# written by John Brearley, Jan 2021 brearley@bell.net

# core item, cant load from dynamic list below.
import importlib

# import required packages from pkg_list
pkg_list=["sys", "time", "traceback", "board", "busio",
          "adafruit_extended_bus", "adafruit_ads1x15.analog_in", "print_data"]
error_list = []
for pkg in pkg_list:
   print("importing pkg: ", pkg)
   try:
      globals()[pkg] = importlib.import_module(pkg)
   except:
      error_list.append(pkg)
      (x,y,z)=sys.exc_info()
      print("ERROR: pkg:", pkg, "msg:", y)# y is root error msg
      # traceback.print_exc() # gives all info, more verbose

if len(error_list) == 0:
   print("Loaded packages OK:", pkg_list)
else:
   print("ERROR: FAILED to load packages:", error_list)
   sys.exit(1)

# One package does NOT load properly via importlib.import_module.
# NB: adafruit_ads1x15.analog_in DOES load OK via importlib.import_module.
try: 
   import adafruit_ads1x15.ads1115
   print ("Loaded packages OK: adafruit_ads1x15.ads1115")
except:
   (x,y,z)=sys.exc_info()
   print("ERROR: misc pkg:", y)# y is root error msg
   sys.exit(1)

print("\n\nglobals:", globals(),"\n\n")

# Create two I2C bus, default & custom #4 per /boot/config.txt
i2c_1 = busio.I2C(board.SCL, board.SDA)
# print_data.print_data(i2c_1)
i2c_4 = adafruit_extended_bus.ExtendedI2C(4) # custom #4 per /boot/config.txt

# Create the ADC objects using two I2C bus
print_data.print_data(adafruit_ads1x15.ads1115.ADS1115)
ads10 = adafruit_ads1x15.ads1115.ADS1115(i2c_1, address=0x48)
print_data.print_data(ads10)
ads13 = adafruit_ads1x15.ads1115.ADS1115(i2c_1, address=0x4b)
print_data.print_data(ads13)
ads40 = adafruit_ads1x15.ads1115.ADS1115(i2c_4, address=0x48)
print_data.print_data(ads40)

# Create single-ended inputs on i2c-1 bus
ch1_48_0 = adafruit_ads1x15.analog_in.AnalogIn(ads10, adafruit_ads1x15.ads1115.P0)
ch1_48_1 = adafruit_ads1x15.analog_in.AnalogIn(ads10, adafruit_ads1x15.ads1115.P1)
ch1_48_2 = adafruit_ads1x15.analog_in.AnalogIn(ads10, adafruit_ads1x15.ads1115.P2)
ch1_48_3 = adafruit_ads1x15.analog_in.AnalogIn(ads10, adafruit_ads1x15.ads1115.P3)

ch1_4b_0 = adafruit_ads1x15.analog_in.AnalogIn(ads13, adafruit_ads1x15.ads1115.P0)
ch1_4b_1 = adafruit_ads1x15.analog_in.AnalogIn(ads13, adafruit_ads1x15.ads1115.P1)
ch1_4b_2 = adafruit_ads1x15.analog_in.AnalogIn(ads13, adafruit_ads1x15.ads1115.P2)
ch1_4b_3 = adafruit_ads1x15.analog_in.AnalogIn(ads13, adafruit_ads1x15.ads1115.P3)

# Create single-ended inputs on i2c-4 bus
ch4_48_0 = adafruit_ads1x15.analog_in.AnalogIn(ads40, adafruit_ads1x15.ads1115.P0)
ch4_48_1 = adafruit_ads1x15.analog_in.AnalogIn(ads40, adafruit_ads1x15.ads1115.P1)
ch4_48_2 = adafruit_ads1x15.analog_in.AnalogIn(ads40, adafruit_ads1x15.ads1115.P2)
ch4_48_3 = adafruit_ads1x15.analog_in.AnalogIn(ads40, adafruit_ads1x15.ads1115.P3)

# Create differential input between channel 0 and 1
#chan = AnalogIn(ads, ADS.P0, ADS.P1)

# ch = [ch0, ch1, ch2, ch3]
print ("I2C    Addr  P Volts     P Volts     P Volts     P Volts")
print ("====== ====  = ========  = ========  = ========  = ========")
while True:
   try:
      # for i,var in enumerate(ch):
      #   print ("i:",i,"var:",var)
      print("I2C: 1 0x48  0 {:>8.5f}  1 {:>8.5f}  2 {:>8.5f}  3 {:>8.5f}".format(ch1_48_0.voltage, ch1_48_1.voltage, ch1_48_2.voltage, ch1_48_3.voltage))
      print("I2C: 1 0x4b  0 {:>8.5f}  1 {:>8.5f}  2 {:>8.5f}  3 {:>8.5f}".format(ch1_4b_0.voltage, ch1_4b_1.voltage, ch1_4b_2.voltage, ch1_4b_3.voltage))
      print("I2C: 4 0x48  0 {:>8.5f}  1 {:>8.5f}  2 {:>8.5f}  3 {:>8.5f}".format(ch4_48_0.voltage, ch4_48_1.voltage, ch4_48_2.voltage, ch4_48_3.voltage))
      time.sleep(0.5)

   except KeyboardInterrupt:
      print("\n\nexiting.")
      break

   except:
      print("Start exception handler")
      traceback.print_exc()
      print("End exception handler")

# values[i] = adc.read_adc(i, gain=GAIN)