Search code examples
pythonpython-3.xazureubuntu-16.04azure-cli2

python on ubuntu linux : json.decoder.JSONDecodeError: Expecting value: line 2 column 6


I'm getting below error when i run python script on Ubuntu 16.04.

It works fine on Windows when I run the same code but not sure which package is not installed properly.

import subprocess
import json

#one vnet and one subnet in the resourcegroup.
def get_vnet_name(resourcegroup):
    get_vnet_command=["az","network","vnet","list","--resource-group",resourcegroup]
    get_vnet=subprocess.run(get_vnet_command, shell = True, stdout=subprocess.PIPE, stderr = subprocess.PIPE)
    a=get_vnet.stdout.decode('utf-8')
    d=json.loads(a)
    for item in d:
        vname=item["name"]
        subnets=item["subnets"]
    for i in subnets:
        subnetname=i["name"]
    return vname,subnetname

def create_vm(vm_resourcegroup,vm_name, vm_image,vm_username, vm_passowrd,vm_vnet,vm_subnet, vm_size):
    create_vm_command=["az","vm","create","--resource-group",vm_resourcegroup,"--name",vm_name,"--image",vm_image, "--admin-username", vm_username,"--admin-password",vm_passowrd,"--vnet-name",vm_vnet,"--subnet",vm_subnet,"--size", vm_size]
    create_vm=subprocess.run(create_vm_command, shell = True, stdout=subprocess.PIPE, stderr = subprocess.PIPE)
    return


if __name__=="__main__":
    rscgroup_name="vm-test-group"
    avm_name="testvm1"
    avm_image="Win2019Datacenter"
    avm_username="myuser"
    avm_password="mypassword"
    avm_size="Standard_D2_V3"
    vault_name = "aqrahkeyvault"
    certificate_name = "staticwebsite"

    avm_vnet,avm_subnet=get_vnet_name(rscgroup_name)
    create_vm(rscgroup_name,avm_name,avm_image,avm_username,avm_password,avm_vnet,avm_subnet,avm_size)

Below is the error which I'm getting related to json.decoder :

  root@linuxvm:/home/azureuser# python3.6  test2.py
    Traceback (most recent call last):
      File "test2.py", line 32, in <module>
        avm_vnet,avm_subnet=get_vnet_name(rscgroup_name)
      File "test2.py", line 9, in get_vnet_name
        d=json.loads(a)
      File "/usr/lib/python3.6/json/__init__.py", line 354, in loads
        return _default_decoder.decode(s)
      File "/usr/lib/python3.6/json/decoder.py", line 339, in decode
        obj, end = self.raw_decode(s, idx=_w(s, 0).end())
      File "/usr/lib/python3.6/json/decoder.py", line 357, in raw_decode
        raise JSONDecodeError("Expecting value", s, err.value) from None
    json.decoder.JSONDecodeError: Expecting value: line 2 column 6 (char 6)

I've tried installing python and that did not resolve the issue.


Solution

  • I tried to reproduce your issue and successfully find out the reason. Actually, your script is correct basically, but you may not consider for a case that the az command not return any result to stdout.

    For example, there is a non-existing resource group in my subscription, such as non-exist-rg. If I pass it as the value of parameter --resource-group, the script below will return error information to stderr, the stdout value is b''.

    import subprocess
    resourcegroup = 'non-exist-rg'
    get_vnet_command=["az","network","vnet","list","--resource-group",resourcegroup]
    get_vnet=subprocess.run(get_vnet_command, shell = True, stdout=subprocess.PIPE, stderr = subprocess.PIPE)
    

    The result of stdout & stderr as below.

    >>> get_vnet.stdout
    b''
    >>> get_vnet.stderr
    b"ERROR: Resource group 'non-exist-rg' could not be found.\r\n"
    

    So if you pass the stdout value to json.loads function, it will throw the same issue as yours, json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0), because json.loads can not process empty content. And in here, the decode function for the bytes value of stdout or stderr is not necessary for json.loads which can received bytes value as the figure below.

    enter image description here

    So to fix it, the solution is to check the stdout value whether be empty or the stderr value whether be not empty.

    def get_vnet_name(resourcegroup):
        get_vnet_command=["az","network","vnet","list","--resource-group",resourcegroup]
        get_vnet=subprocess.run(get_vnet_command, shell = True, stdout=subprocess.PIPE, stderr = subprocess.PIPE)
        # decode for stdout is not necessary
        # a=get_vnet.stdout.decode('utf-8')
        vname,subnetname = '', ''
        if get_vnet.stdout == b'':
            d=json.loads(get_vnet.stdout)
            for item in d:
                vname=item["name"]
                subnets=item["subnets"]
            for i in subnets:
                subnetname=i["name"]
        return vname,subnetname
    

    Then, you need to check the vname & subnetname values before invoke create_vm method.

    Hope it helps.