1

There are two functions:

  • ReadInp() -> Dict[str, str] This reads a csv and returns a dict with a url as key and a payload as the value.
  • ReadTUsr() -> List[List[str, str]] This reads a separate csv and returns 'sample users' as a list of lists. Each inner list is an id type and an id value. For instance:

[['uId', 'T1'], ['srId', 'T2'], ['siId', 'T3'], ['siId', 'T4'], ['srId', 'T5']...]

I need to make url calls that join these two values. The url being called will come from the first return value, but it must splice in the sample user from the second list. These lists may not be the same size, and if the second is exhausted before the first it should iterate again over the list until the first is exhausted.

For instance, the resultant calls would be like:

http://localhost:8080/cabApp/callcab?uId=T1 then POST body: {'location': '', 'ppl': '2', 'text': 'MyCab'}

Or:

http://localhost:8080/cabApp/call3cab?srId=T2 then POST body: {'display': 'RMP', 'time': '', 'location': 'LA'}

This is what I have tried:

def makeAcall():
    r = ReadInp() # gives dict with endpoint as key and payload as Value  
    t = ReadTUsr() # give nested user list 
    print(t)
    print(type(r))

    #for k1,v1 in r.items():
    #    for i,j in t:
    
    for (k1,v1), (i,j) in zip(r.items(), t):

        url = f"{AURL}(k1)}"
        rj = v1
        #uid = {"uId": "T1"}
        headers = {'Accept': "application/json"}
        res = requests.post(url, json=rj, headers=headers, params=(i,j))

        print("Req Resp Code:",res.status_code)
        #api_req_res_text.append(res.text)
        api_req_res_code.append(res.status_code)
    
    return api_req_res_code
  • Note1: This is how my dict looks like {'cabApp/callcab1': {'location': '', 'ppl': '2', 'text': 'MyCab'}, 'cab2App/call2cab2': {'ppl': '', 'text': 'Cab2'}, 'cabApp/call3cab': {'display': 'RMP', 'time': '', 'location': 'LA'}...} Here cabApp/callcab1, cab2App/call2cab2 & cabApp/call3cab are keys, which are endpoints[iterated as k1 in code] and rest all is their individual payloads [iterated as v1] respectively. http://localhost:8080 part is stored in AURL and is static. I can not update the code which builds the API as per the instruction[res where I am making the post request]. What I am searching for is the way to iterate list and use it as value after param.
  • Note2: We can change the way it read sample users csv. Right now I am using this code to read sample users csv which gives nested list.
def ReadTUsr():
    with open(T_U_csv, 'r') as file:
        csv_file = csv.reader(file)
        data = [it for it in csv_file]
        #print(data)
        return data 
  • Note3: Earlier ReadTUsr() was dict and was working as expected (was able to iterate and call new sample user for each request) but latest code has duplicate keys in dict (multiple times siId & srId) so it only takes one record from duplicate keys which is default python behavior.
  • Que How to iterate sample users (i,j) along with endpoint & payload (k1,v1) while calling each post request for res variable.
Finch
  • 71
  • 1
  • 9

2 Answers2

2

The easiest way to do this is to enforce your sample user list is equal in length to your url list:

def equalize_lists(users: list, desired_length) -> list:
    if len(users) > desired_length:
        return users[:desired_length]
    else:  # We need to extend the user list
        repeat, extra = (desired_length // len(users), desired_length % len(users))
        for _ in range(repeat):
            users.extend(users)
        users.extend(users[:extra]
        return users

def create_test_calls():
    urls = ReadInp()
    test_users = ReadTUsr()

    users = equalize_lists(test_users, len(urls)
    for ((url, payload), (user_type, user_id)) in zip(urls.items(), users):
        endpoint = f"{AURL}{url}"
        ... etc.

It's a bit unclear how exactly you're constructing your endpoint, but at the point here where endpoint = ... you should have the elements you need, properly associated as url, payload, user_type and user_id. I do recommend using descriptive names! It can make for easier debugging.

Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
  • 1
    You could simplify this dramatically with sequence multiplication: `users *= repeat + (extra > 0)`, removing the loop and repeated `extend` calls. You could also compute `repeat, extra = divmod(desired_length, len(users))` to do the division/length-checking work only once. It ends up a little longer than necessary (it will end with a complete repeat of the input, even if only one element is used), but `zip` stops when any argument is exhausted anyway. In real code though, I'd stick to `itertools.cycle`, which doesn't involve multiplying the overhead to store the elements. – ShadowRanger Apr 27 '22 at 20:55
  • @ShadowRanger Fair enough! I wasn't familiar with `cycle` but that does seem exactly what you want! – Nathaniel Ford Apr 27 '22 at 21:01
  • https://stackoverflow.com/users/442945/nathaniel-ford I have used your code as is. actual call looks like `res = requests.post(url, json=rj, headers=headers, params=(user_type, user_id))` But it gives me error `ValueError: too many values to unpack (expected 2)` . am I missing something? – Finch Apr 28 '22 at 18:44
  • What line causes that? – Nathaniel Ford Apr 28 '22 at 18:47
  • https://stackoverflow.com/users/364696/shadowranger I have update `def equalize_lists` as per you comment but then also I am getting same error i.e `valueError: too many values to unpack (expected 2)` – Finch Apr 28 '22 at 19:03
  • @Maddy9 'Too many values to unpack' usually means that an assignment into a tuple is failing because the source has more items in it's collection than there are corresponding values in the tuple. For instance if `(url, payload) = ReadInp()` has that error, it means `ReadInp()` is returning a tuple or list with more than two items in it. I don't know the 'shape' of those outputs but that is where I would look first. – Nathaniel Ford Apr 28 '22 at 21:34
  • https://stackoverflow.com/users/442945/nathaniel-ford I am Still stuck on the same point. I have raised this as new question with more details. Added how my dict outputs looks like. Is it possible to you to look it again when you get some time? Que Link : https://stackoverflow.com/questions/72048561/how-to-iterate-sample-users-along-with-endpoint-payload-while-calling-each-api – Finch May 09 '22 at 07:17
2

You can use itertools.cycle to cycle the user list:

from itertools import cycle

# 20 uris with different ports usable for str.format() by supplying 
# with u=uri and v=value
uris = [f"http://localhost:{port}/cabApp/callcab?{{u}}={{v}}" 
        for port in range(10000,10020)]

# u,v lists / tuples whatever
users = [['uId', 'T1'], ['srId', 'T2'], ['siId', 'T3'], 
         ['siId', 'T4'], ['srId', 'T5']]

# cycles around if at end
user_cycle = cycle(users)

# zip exhausts to the smaller list - wich is always uris now
# as user_cycle is "endless"
for idx, (uri, user) in enumerate( zip(uris,user_cycle), 1 ):
    print(f"{idx:>2})", uri.format(u=user[0],v=user[1]))
    

Output:

 1) http://localhost:10000/cabApp/callcab?uId=T1
 2) http://localhost:10001/cabApp/callcab?srId=T2
 3) http://localhost:10002/cabApp/callcab?siId=T3
 4) http://localhost:10003/cabApp/callcab?siId=T4
 5) http://localhost:10004/cabApp/callcab?srId=T5
 6) http://localhost:10005/cabApp/callcab?uId=T1
 7) http://localhost:10006/cabApp/callcab?srId=T2
 8) http://localhost:10007/cabApp/callcab?siId=T3
 9) http://localhost:10008/cabApp/callcab?siId=T4
10) http://localhost:10009/cabApp/callcab?srId=T5
11) http://localhost:10010/cabApp/callcab?uId=T1
12) http://localhost:10011/cabApp/callcab?srId=T2
13) http://localhost:10012/cabApp/callcab?siId=T3
14) http://localhost:10013/cabApp/callcab?siId=T4
15) http://localhost:10014/cabApp/callcab?srId=T5
16) http://localhost:10015/cabApp/callcab?uId=T1
17) http://localhost:10016/cabApp/callcab?srId=T2
18) http://localhost:10017/cabApp/callcab?siId=T3
19) http://localhost:10018/cabApp/callcab?siId=T4
20) http://localhost:10019/cabApp/callcab?srId=T5
Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • https://stackoverflow.com/users/7505395/patrick-artner Apologize for being noob. I tried modifying my existing code the way you suggested. `for (k1,v1) in enumerate(zip(r.items(), t)): #for ((k1, v1), (i, j)) in zip(r.items(), cycle(t)): url = f"{AURL}(k1)}" rj = v1 #uid = {"uId": "T1"} headers = {'Accept': "application/json"} response = requests.post(f_url, json=req_json, headers=headers, params={{i}}={{j}})` but ain't got the expected result. Can you please tell me what I am doing wrong? – Finch Apr 28 '22 at 17:39
  • @MAddy unfortunately comments don'T allow code in a structured way. Consider asking a new question with your actual demo code, linking back to this question or/and my answer. – Patrick Artner Apr 28 '22 at 18:07
  • https://stackoverflow.com/users/7505395/patrick-artner As per your suggestion I've raised this as new thread with more details. Could you please look into this when you get some spare time? TIA [Link: https://stackoverflow.com/questions/72048561/how-to-iterate-sample-users-along-with-endpoint-payload-while-calling-each-api] – Finch May 09 '22 at 07:18