Q: How to Use Python to Send a Multipart PDF Request to OneNote I'm trying to upload a PDF to OneNote using Python. According to the OneNote API, I need to post a request like this:
Content-Type:multipart/form-data; boundary=MyAppPartBoundary
Authorization:bearer tokenString

--MyAppPartBoundary
Content-Disposition:form-data; name="Presentation"
Content-type:text/html

<!DOCTYPE html>
<html>
  <head>
    <title>A page with an embedded and displayed PDF file</title>
  </head>
    <body>
      <p>Attached is the lease agreement for the expanded offices!</p>
      <object 
        data-attachment="OfficeLease.pdf" 
        data="name:OfficeLeasePartName" 
        type="application/pdf" />
      <p>Here's the contents of our new lease.</p>
      <img data-render-src="name:OfficeLeasePartName" width="900"/>
    </body>
</html>

--MyAppPartBoundary
Content-Disposition:form-data; name="OfficeLeasePartName"
Content-type:application/pdf

... PDF binary data ...

--MyAppPartBoundary--

However, I have no idea how to do a multipart request in Python. I can do a basic text/html request just fine though:
url = ROOT_URL+"pages"

headers = {"Content-Type":"text/html",
           "Authorization" : "bearer " + access_token}

# Format html (title & text)

html = "<html><head><title>" + title + "</title></head>"
html += "<body><p>" + text + "</p></body></html>"

# Send request

session = requests.Session()
request = requests.Request(method="POST", headers=headers,
                           url=url,  data=html)
prepped = request.prepare()
response = session.send(prepped)

How would I modify that Python code for multi-part?
[########### UPDATE ############]
Based on jayongg's suggestion, I tried the following. When I do, the error I get switches from "Page create requests require the content to be multipart, with a presentation part" to "The multi-part payload was malformed." I think it's because I'm not actually attaching the pdf file somewhere? I'm also not sure what's the difference between OfficeLease.pdf in the OneNote api example and OfficeLeasePartName.
Here is my current code:
url = ROOT_URL+"pages"

path = os.path.join(pdfFolder, pdfName + ".pdf")   

headers = {"Content-Type":"multipart/form-data; boundary=MyAppPartBoundary",
           "Authorization" : "bearer " + access_token}

f = open(path, "rb").read()

txt = """--MyAppPartBoundary
        Content-Disposition:form-data; name="Presentation"
        Content-type:text/html

        <!DOCTYPE html>
        <html>
          <head>
            <title>A page with an embedded and displayed PDF file</title>
          </head>
            <body>
              <p>Attached is the lease agreement for the expanded offices!</p>
              <object 
                data-attachment="Sample5.pdf" 
                data="name:Sample5" 
                type="application/pdf" />
              <p>Here's the contents of our new lease.</p>
              <img data-render-src="name:Sample5" width="900"/>
            </body>
        </html>

        --MyAppPartBoundary
        Content-Disposition:form-data; name="OfficeLeasePartName"
        Content-type:application/pdf
        """ + f + """
        --MyAppPartBoundary--"""

session = requests.Session()
request = requests.Request(method="POST", headers=headers,
                           url=url,  data=txt)
prepped = request.prepare()
response = session.send(prepped)

[########## UPDATE 2 ##############]
If I make the code even simpler, it still results in a malformed error:
headers = {"Content-Type":"multipart/form-data; boundary=MyAppPartBoundary",
           "Authorization" : "bearer " + access_token}

txt = """--MyAppPartBoundary
        Content-Disposition:form-data; name="Presentation"
        Content-type:text/html

        <!DOCTYPE html>
        <html>
          <head>
            <title>One Note Text</title>
          </head>
            <body>
              <p>Hello OneNote World</p>
            </body>
        </html>

        --MyAppPartBoundary--
        """

session = requests.Session()
request = requests.Request(method="POST", headers=headers,
                           url=url,  data=txt)

I've also tried like this. Same thing:
headers = {"Content-Type":"multipart/form-data; boundary=MyAppPartBoundary",
           "Authorization" : "bearer " + access_token} 

txt = """<!DOCTYPE html>
        <html>
          <head>
            <title>One Note Text</title>
          </head>
            <body>
              <p>Hello OneNote World</p>
            </body>
        </html>"""    

files = {'file1': ('Presentation', txt, 'text/html')}

session = requests.Session()
request = requests.Request(method="POST", headers=headers,
                           url=url,  files=files)
prepped = request.prepare()
response = session.send(prepped)


A: The answer it turns out is that Python encodes blank lines as "\n" but OneNote requires "\r\n". It also requires a blank line ("\r\n") after the final boundary. Finally, it can't have any leading whitespace in the body (no indents) for the Content-Type and Content-Disposition lines. (There should also be a blank line after each Content-Disposition line.)
For example, if this was the body:
"""--MyBoundary
Content-Type: text/html
Content-Disposition: form-data; name="Presentation"

Some random text
--MyBoundary
Content-Type: text/text
Content-Disposition: form-data; name="more"; filename="more.txt"

More text
--MyBoundary--
"""

it should be represented by the string
'--MyBoundary\r\nContent-Type: text/html\r\nContent-Disposition: form-data; name="Presentation"\r\n\r\nSome random text\r\n--MyBoundary\r\nContent-Type: text/text\r\nContent-Disposition: form-data; name="more"; filename="more.txt"\r\n\r\nMore text\r\n--MyBoundary--\r\n' 

which can be made just by typing the text inside three """ quotes (which will automatically create \n in the final string wherever there's a blank line) and then replacing the "\n" with "\r\n":
body = body.replace("\n", "\r\n")

The headers would be:
headers = {"Content-Type":"multipart/form-data; boundary=MyBoundary",
           "Authorization" : "bearer " + access_token} 

Finally, you would POST the call like this:
session = requests.Session()
request = requests.Request(method="POST", headers=headers,
                           url=url,  data=body)
prepped = request.prepare()
response = session.send(prepped)


A: You need to construct the full HTTP request, not just send along the HTML.
For your body, try constructing the full body as you posted in your question.
--MyAppPartBoundary
Content-Disposition:form-data; name="Presentation"
Content-type:text/html

<!DOCTYPE html>
<html>
  // truncated
</html>

--MyAppPartBoundary
Content-Disposition:form-data; name="OfficeLeasePartName"
Content-type:application/pdf

... PDF binary data ...

--MyAppPartBoundary--

Make sure you set the content-type header correctly:
Content-Type:multipart/form-data; boundary=MyAppPartBoundary

