NoneType object has no attribute json - Office365 file upload using Python

233 Views Asked by At

I am trying to use the Office365-REST module to upload a file to Sharepoint.

from office365.runtime.auth.authentication_context import AuthenticationContext
from office365.sharepoint.client_context import ClientContext
from office365.sharepoint.files.creation_information import FileCreationInformation
import traceback

try:
    fullurl = 'https://org.sharepoint.com/sites/SiteName/'
    fileName = 'c:/temp/upload.zip'
    rootfolder = 'Documents'
    targetfolder = '/PythonTests/'

    ctx_auth = AuthenticationContext(url=fullurl)
    if  ctx_auth.acquire_token_for_app(client_id='redacted', client_secret='redacted'):
        ctx = ClientContext(fullurl, ctx_auth)
        target_list = ctx.web.lists.get_by_title(rootfolder)
        info = FileCreationInformation()
        with open(fileName, 'rb') as content_file:
            info.content = content = content_file.read()
        info.url = fileName
        info.overwrite = True
        upload_file = target_list.root_folder.files.add(fileName.split("/")[-1], info.content, overwrite=True)
        ctx.execute_query()
except:
    traceback.print_exc()

Exception has occurred: AttributeError
'NoneType' object has no attribute 'json'
  File "C:\scripts\upload_to_sharepoint.py", line 20, in <module>
    ctx.execute_query()
AttributeError: 'NoneType' object has no attribute 'json'

Traceback:

Traceback (most recent call last):
  File "C:\Code\Python\Office365-REST\upload_to_sharepoint.py", line 22, in <module>
    ctx.execute_query()
  File "C:\Python\Python38\lib\site-packages\office365\runtime\client_runtime_context.py", line 181, in execute_query
    self.pending_request().execute_query(qry)
  File "C:\Python\Python38\lib\site-packages\office365\runtime\client_request.py", line 58, in execute_query
    response = self.execute_request_direct(request)
  File "C:\Python\Python38\lib\site-packages\office365\runtime\client_request.py", line 70, in execute_request_direct
    self.beforeExecute.notify(request)
  File "C:\Python\Python38\lib\site-packages\office365\runtime\types\event_handler.py", line 21, in notify
    listener(*args, **kwargs)
  File "C:\Python\Python38\lib\site-packages\office365\sharepoint\client_context.py", line 221, in _build_modification_query     
    self._ensure_form_digest(request)
  File "C:\Python\Python38\lib\site-packages\office365\sharepoint\client_context.py", line 157, in _ensure_form_digest
    self._ctx_web_info = self._get_context_web_information()
  File "C:\Python\Python38\lib\site-packages\office365\sharepoint\client_context.py", line 170, in _get_context_web_information  
    client.map_json(response.json(), return_value, json_format)
AttributeError: 'NoneType' object has no attribute 'json'

I noticed that the request.auth was None, and there are no headers listed in request.headers when the post is made to the /contextInfo URL. However, I am authenticated according to the AuthenticationContext return. I'm also successfully getting the target_list. I'm unsure where to go from here. Thank you in advance for any help.

EDIT

Updated the code above based on the suggestion for {fullurl} on line 14.
I decided to use the Graph API Explorer to test some methods and noticed a couple of items which may be influencing this issue. First, when I do a GET request to the site drives, there is nothing in the values list.

GET https://graph.microsoft.com/v1.0/sites/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/drives

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#drives",
    "value": []
}

Without identifying a drive, the library name wouldn't resolve, correct?

If I try to retrieve the details of the default drive, I get access is denied.

GET https://graph.microsoft.com/v1.0/sites/root:/sites/SiteName:/drive

{
    "error": {
        "code": "accessDenied",
        "message": "Access denied",
        "innerError": {
            "date": "2023-08-01T16:49:31",
            "request-id": "9b9b9ff7-cd93-4a55-8f8c-14035c3f03e2",
            "client-request-id": "f0761f22-91af-8740-cdfa-987715aeab89"
        }
    }
}

I am not the owner of the Sharepoint group, so I believe the issue stems from my permisisons. I am working to get elevated permissions to see if that addresses anything.

EDIT #2

Following the suggestion to use a more recent version of the API, I updated and tried the following, but still received the JSON error.

import os

from office365.sharepoint.client_context import ClientContext
from office365.runtime.auth.client_credential import ClientCredential


def print_upload_progress(offset):
    file_size = os.path.getsize(local_path)
    print("Uploaded '{0}' bytes from '{1}'...[{2}%]".format(offset, file_size, round(offset / file_size * 100, 2)))


ctx = ClientContext('https://org.sharepoint.com/sites/SiteName/').with_credentials(ClientCredential(client_id='redacted', client_secret='redacted'))

target_url = "Shared Documents/PythonTests"
target_folder = ctx.web.get_folder_by_server_relative_url(target_url)
size_chunk = 1000000
local_path = "c:/temp/upload.zip"
with open(local_path, 'rb') as f:
    uploaded_file = target_folder.files.create_upload_session(f, size_chunk,
                                                              print_upload_progress).execute_query()

print('File {0} has been uploaded successfully'.format(uploaded_file.serverRelativeUrl))

Traceback (most recent call last):
  File "C:\Code\Python\Office365-REST\upload_to_sharepoint_user_updated.py", line 24, in <module>
    uploaded_file = target_folder.files.create_upload_session(f, size_chunk,
  File "C:\Python\Python38\lib\site-packages\office365\runtime\client_object.py", line 44, in execute_query
    self.context.execute_query()
  File "C:\Python\Python38\lib\site-packages\office365\runtime\client_runtime_context.py", line 181, in execute_query
    self.pending_request().execute_query(qry)
  File "C:\Python\Python38\lib\site-packages\office365\runtime\client_request.py", line 57, in execute_query
    response = self.execute_request_direct(request)
  File "C:\Python\Python38\lib\site-packages\office365\runtime\client_request.py", line 69, in execute_request_direct
    self.beforeExecute.notify(request)
  File "C:\Python\Python38\lib\site-packages\office365\runtime\types\event_handler.py", line 21, in notify
    listener(*args, **kwargs)
  File "C:\Python\Python38\lib\site-packages\office365\sharepoint\client_context.py", line 222, in _build_modification_query
    self._ensure_form_digest(request)
  File "C:\Python\Python38\lib\site-packages\office365\sharepoint\client_context.py", line 157, in _ensure_form_digest
    self._ctx_web_info = self._get_context_web_information()
  File "C:\Python\Python38\lib\site-packages\office365\sharepoint\client_context.py", line 171, in _get_context_web_information
    client.map_json(response.json(), return_value, json_format)
AttributeError: 'NoneType' object has no attribute 'json'

The same error is thrown. I did some more digging and I noticed this site is made from a Microsoft Team, and now I'm wondering if the structure of a Sharepoint Site made by Teams differs from a non-Team site?

EDIT #3

I have replicated the issue on a completely different Sharepoint site, and ruled out the possibility it was because Teams created a site. We tested on new sites created both ways in the second Sharepoint site, and still there are issues with the request being made from the module. At this point, I am looking to change to PowerShell and hope this get around the issues that are being presented by the Office365 module.

1

There are 1 best solutions below

6
On

EDIT - Answer irrelevant, OP updated the code, still having issues.


On line 14, instantiating ClientContext, a literal {fullurl} is provided as the first argument, while an actual URL is expected there (e.g. https://org.sharepoint.com/...).

To provide it with the actual URL as provided to AuthenticationContext, simply remove the single-quotation marks (') and the curly-brackets ({}), like so:

        ctx = ClientContext(fullurl, ctx_auth)

In case that f-string parsing is somehow required (although not demonstrated in the question) - an f should be prepended to the literal string, like so:

        ctx = ClientContext(f'{fullurl}', ctx_auth)