in the search bar now to to find the search bar component what I'm going to


Streamlit for ML #1 - Installation and API

do is type in Streamlit components and I want to go to the dots so here and


actually maybe not Streamlit components if I come down to API reference and we


can go down and we can look for what we need so we want like a search box the


text search box so we have text input here okay so we're going to copy that


I'm not sure if you can see that very well or not I zoom in a little bit more


yeah so I'm going to write query equals st text input and I'm just going to put


something like search okay and in here so you can't see on the on the left here


but there's also a default argument now I'm going to enter that in there so the


default value in there just going to be an empty string now this will be useful


later on but for now we don't actually need that and if I let's open the


Streamlit app I'm going to save this on the right it says source file change


rerun now we can rerun it once and what we'll do is like refresh the page


rerun your app I want to always rerun so what this is going to do is


automatically reload my app every time I save the file on the right so that means


that we can basically prototype and build something super fast now the first


thing I want to show you here is what happens when we type something in query


so I'm going to use st write again so st write and I'm just going to write and


use an f-string here I'm going to put query is equal to whatever is in query


here okay I'm going to save that it will refresh so at the moment we see a query


is just equal to an empty string now what if we ask a question now this isn't


going to work yet because we haven't set up the whole the backend logic yet but


we'll put let's put hello world okay now as soon as I press enter there the


whole Streamlit app re-executed and rerun okay from top to bottom we run the full


code now when you have parts of your app that take a little while to load that


can be a bit annoying although there are ways around that which we we will


explore in the future but for now that's pretty much all we want to do for a


brief introduction in the next video what we're going to do is actually have


a look at integrating the smart part of the app so what you start to start with


the vector database and the retrieval models we're going to integrate those


into this so that's it for now thank you very much for watching and I'll see you


Intro to APIs in Python - API Series #1

Hi, welcome to the video. We're doing something slightly different this time.


Intro to APIs in Python - API Series #1

We are going to put together a series of videos just covering APIs, what APIs are,


Intro to APIs in Python - API Series #1

how we interact with them, how we build them. And we'll have a look at a few different Python


Intro to APIs in Python - API Series #1

frameworks that we can use for putting them together. Now, what I have tried to visualize


Intro to APIs in Python - API Series #1

here is how we can think of APIs. So API, to start with, stands for Application Program Interface.


Intro to APIs in Python - API Series #1

And we can think of APIs as a black box. So this thing in the middle here is our API.


Intro to APIs in Python - API Series #1

Now, inside this magical black box, what we do is we send data to it over here.


Intro to APIs in Python - API Series #1

And all we really know is that it sends data back to us over here. Now, in reality, this API here is


Intro to APIs in Python - API Series #1

a script on a server somewhere. And what it is doing is acting as a middleman or a gatekeeper


Intro to APIs in Python - API Series #1

between us and the server that it represents. Now, through this API, we can typically download


Intro to APIs in Python - API Series #1

or get information. We can request to change some information over on the server, or we can request


Intro to APIs in Python - API Series #1

to delete information on the server. And most requests, we can kind of fit into one of those


Intro to APIs in Python - API Series #1

three things. We're either getting information, updating, or putting information, or deleting


Intro to APIs in Python - API Series #1

information. Now, APIs are very structured in that they have a very specific way of working.


Intro to APIs in Python - API Series #1

So they generally all follow the same structure. And the most common structure out there is called


Intro to APIs in Python - API Series #1

a RESTful API. Now, a RESTful API, or REST, stands for Representational State Transfer.


Intro to APIs in Python - API Series #1

And that sounds really complicated. In reality, it's very straightforward. We don't really need to


Intro to APIs in Python - API Series #1

go into too much depth, but it consists of six key factors. So we need to follow these six rules


Intro to APIs in Python - API Series #1

in order to make sure our API is RESTful or a REST API. So the first of those is we use a single


Intro to APIs in Python - API Series #1

outward-facing interface. So there's a single entry point for us to communicate with this API.


Intro to APIs in Python - API Series #1

There should be client-server independence. So like we saw before, over here on the left,


Intro to APIs in Python - API Series #1

that is the internet. And that is where we are coming from. Over here is a server.


Intro to APIs in Python - API Series #1

And what this API does is acts as a middleman between both of those, and it allows those two to,


Intro to APIs in Python - API Series #1

for example, if the client gets updated, it will not break the connection between the client and


Intro to APIs in Python - API Series #1

server. All we need to do is communicate with the API. So our communications to the API should not


Intro to APIs in Python - API Series #1

change. Now, if the server is updated and things change on the server, the API should remain the


Intro to APIs in Python - API Series #1

same. And therefore, when we try to communicate with it, nothing will change. And then we also


Intro to APIs in Python - API Series #1

have statelessness. So what this means is, say you send a API request, and then we send another API


Intro to APIs in Python - API Series #1

request immediately after. The results of that second API request should not rely or should not


Intro to APIs in Python - API Series #1

be dependent or change based on the results of the first request, unless, of course, we're changing


Intro to APIs in Python - API Series #1

or deleting information and requesting all of that information back again. Of course, in that case,


Intro to APIs in Python - API Series #1

it will change, but the actual state of the API will not change. All that's changing there is the


Intro to APIs in Python - API Series #1

underlying data that is hosted on the server is changing, but the state of the API does not change.


Intro to APIs in Python - API Series #1

It's a blank slate at the start of every new request. For caching, so down here, all we're


Intro to APIs in Python - API Series #1

saying here is that the API must, it needs to inform us whether its responses are allowed to be


Intro to APIs in Python - API Series #1

cached by the user. The next rule is that the API should follow a layered system structure. So


Intro to APIs in Python - API Series #1

there should be a modular structure to this API. So one layer can be changed and it should not affect


Intro to APIs in Python - API Series #1

the other layers. And finally, where it's applicable, the API should be able to provide code,


Intro to APIs in Python - API Series #1

executable code on request. Now, not all APIs are relevant to this, so we're not going to see this


Intro to APIs in Python - API Series #1

everywhere. And as far as I know, it's reasonably rare as well. So I wouldn't worry too much about


Intro to APIs in Python - API Series #1

that. But all of this together for us as general users just means there'll be a single interface


Intro to APIs in Python - API Series #1

for us to interact with the API through, and there are a specific set of behaviors that we would


Intro to APIs in Python - API Series #1

expect. So we wouldn't expect this API to deviate too much from any other API. Now there are a set


Intro to APIs in Python - API Series #1

of different methods that we can interact with an API with. So we have a GET request, which is where


Intro to APIs in Python - API Series #1

we retrieve information, a POST request for creating a resource or basically creating a new


Intro to APIs in Python - API Series #1

record on the other side of the API. We have a PUT request for updating a resource or record,


Intro to APIs in Python - API Series #1

and then we have DELETE for deleting an existing resource or record. Now, the most commonly used


Intro to APIs in Python - API Series #1

of these is the GET request. We use that quite a lot, to be honest. Whenever you want information


Intro to APIs in Python - API Series #1

from an API, you use a GET request. And then if you are interacting with the API in some way,


Intro to APIs in Python - API Series #1

there's a database that you're modifying behind it, you're going to use POST, PUT, and DELETE quite


Intro to APIs in Python - API Series #1

a lot as well. So for example, if we wanted to get the GPS coordinates from the Google Maps API,


Intro to APIs in Python - API Series #1

we would use the GET request. So we'd be requesting information. Alternatively, if we were going to


Intro to APIs in Python - API Series #1

use the GitHub API and we wanted to create a repository, we wanted to update a repository,


Intro to APIs in Python - API Series #1

we wanted to update a repository, or we wanted to delete a repository, we would be using one of those


Intro to APIs in Python - API Series #1

three. Now, there is also another, well, there are quite a few methods, but there is another one


Intro to APIs in Python - API Series #1

that I see online. So I mean, the PATCH request is for partial updates. So it's like a PUT request,


Intro to APIs in Python - API Series #1

but for partial updates. Now, I've never actually seen this used. So I mean, I'm sure it's used


Intro to APIs in Python - API Series #1

somewhere. And if you use APIs a lot, then maybe you'll use it. But I think for most users,


Intro to APIs in Python - API Series #1

you're probably not going to come across it. Okay, now we've covered how we interact or what


Intro to APIs in Python - API Series #1

requests we send to an API. Now, what about what it returns to us? Now, an API will return different


Intro to APIs in Python - API Series #1

codes depending on the output or the, not output, the result of whatever it is we asked it to do.


Intro to APIs in Python - API Series #1

So the first set of codes here are the 200 success codes. So anything within the 200 range is usually


Intro to APIs in Python - API Series #1

a success or it's a good thing. Now, we have the most common one, which is 200 OK, which just means


Intro to APIs in Python - API Series #1

success in general. 201, we created something. Or 204, we, there's a success, but the API didn't


Intro to APIs in Python - API Series #1

return anything. It's not necessarily a problem. And then we have the 400 codes. Now these are


Intro to APIs in Python - API Series #1

client side errors, so errors on our side. And we have the most common, actually, to be honest,


Intro to APIs in Python - API Series #1

all these are pretty common. The one that you, means that you're probably doing something wrong


Intro to APIs in Python - API Series #1

is 400 is a bad request. It means we're entering the request wrong. So that might be a, the syntax


Intro to APIs in Python - API Series #1

is wrong. The JSON format is wrong. Or we are using the wrong fields in our request. And then


Intro to APIs in Python - API Series #1

we have also unauthorized. Typically it's because we're not authorized ourselves with an authorization


Intro to APIs in Python - API Series #1

key. Forbidden, so you're trying to access something that you're not allowed to access.


Intro to APIs in Python - API Series #1

Again, that might be because you're unauthorized and you just need to enter your auth key and then


Intro to APIs in Python - API Series #1

you'll be allowed there. And then also 404, which is not found. So that means there's nothing there.


Intro to APIs in Python - API Series #1

Although some websites like GitHub will use this when there is actually, for example, a repo there,


Intro to APIs in Python - API Series #1

but it's a secret and you're not allowed to see it. They'll give you a 404 because otherwise


Intro to APIs in Python - API Series #1

they're telling you, hey, look, this actually exists. So they also give you that sometimes


Intro to APIs in Python - API Series #1

instead of forbidden or unauthorized. And then there's also these ones. So these are


Intro to APIs in Python - API Series #1

probably the two most important client error codes. So there's 418, which is I'm a teapot.


Intro to APIs in Python - API Series #1

And that's when, you know, sometimes it happens where you ask a teapot to brew coffee. So in that


Intro to APIs in Python - API Series #1

case, just stop doing it. And there's also 420 enhance your comm. So that's specific to Twitter.


Intro to APIs in Python - API Series #1

And that's just saying you really need to chill. And because you're sending far too many requests


Intro to APIs in Python - API Series #1

to Twitter. Okay. And that's it for the codes and let's move on to the JSON object. So this is the


Intro to APIs in Python - API Series #1

format we use for interacting with our API. It stands for JSON object notation. And I mean,


Intro to APIs in Python - API Series #1

you can probably tell from this, if you use Python, it looks like a dictionary and it


Intro to APIs in Python - API Series #1

pretty much is a dictionary. They use the exact same structure, although they're not


Intro to APIs in Python - API Series #1

technically the same. But a dictionary is a JSON like object. Now it's just a hierarchical format.


Intro to APIs in Python - API Series #1

It allows us to use all these different fields. We can put lists with strings, put more dictionaries


Intro to APIs in Python - API Series #1

inside it. So it's pretty useful. And this is the standard format that's used by everyone for APIs.


Intro to APIs in Python - API Series #1

Now I can show you an example of this. So I just come over here. I have this little link here. So


Intro to APIs in Python - API Series #1

this is actually we're going to go to an API in the browser and make a request from the browser.


Intro to APIs in Python - API Series #1

So we can do that. There's nothing weird about doing that. And we've gone


Intro to APIs in Python - API Series #1

to this. It's called the Poke API. So it's actually an API for Pokemon. And it just returns


Intro to APIs in Python - API Series #1

you all this information. You can see that, I mean, it looks pretty messy because it's not


Intro to APIs in Python - API Series #1

a clean format. But this is basically just a dictionary. And this is a JSON format.


Intro to APIs in Python - API Series #1

Now let's go back to our code and we're going to start putting something together. So we're


Intro to APIs in Python - API Series #1

going to start making some requests. So we first want to import requests. This is just a standard


Intro to APIs in Python - API Series #1

library for making API requests in Python. It's super easy to use. So we just import requests.


Intro to APIs in Python - API Series #1

And to make a GET request, all we need to do is write request.get. And let's use the Pokemon API


Intro to APIs in Python - API Series #1

that we saw before. So I'm going to copy this. In fact, I already copied it. No, I didn't.


Intro to APIs in Python - API Series #1

So I'm going to copy this and just bring it down here. So enter is a string.


Intro to APIs in Python - API Series #1

And let me, I'm going to store this in the response variable there. And let's see what we get. So we


Intro to APIs in Python - API Series #1

get a 200 response. So remember before. That's what we see over here. So it's the 200 OK response. So


Intro to APIs in Python - API Series #1

it's good. It means it went OK. It went well. But that's all we see. So how do we actually access


Intro to APIs in Python - API Series #1

the JSON response underneath that? All we need to do is write JSON like that. So we use the JSON


Intro to APIs in Python - API Series #1

method. And that's all there is to it. So now we see something very similar to what we've got in


Intro to APIs in Python - API Series #1

the browser before. So we have this dictionary now. We have these abilities. I'm not sure exactly


Intro to APIs in Python - API Series #1

what this is returning. I think it's just returning. Why is it returning? Oh, so we're searching for


Intro to APIs in Python - API Series #1

Charizard. So Charizard has several abilities. Blaze, solar power, and lots of other things.


Intro to APIs in Python - API Series #1

Forms. OK, so I'm not sure why you use that. But so if we want to access, we can access abilities


Intro to APIs in Python - API Series #1

there. OK, and it goes in to what is now a list. So now we need to access first end level of that


Intro to APIs in Python - API Series #1

list. And we have this other, this one entry here. Go further. So you see it's basically just a sort


Intro to APIs in Python - API Series #1

of a tree structure. We just keep going deeper and deeper in there. And we can. Let's go. Let's get


Intro to APIs in Python - API Series #1

the URL. Just out of curiosity, I really want to see what is there. So, OK. So it's just another


Intro to APIs in Python - API Series #1

is just another is at the API. We can see that because it says up here I'll zoom in at


Intro to APIs in Python - API Series #1

pokeapi.co and then slash API. Just very curious. I wonder if I can access the website directly.


Intro to APIs in Python - API Series #1

Like ability 66. No, we can't. OK, no problem. Yeah, so we have that. So maybe that's useful if


Intro to APIs in Python - API Series #1

you're really super into Pokemon. I don't know. Maybe like it for a game or something could be


Intro to APIs in Python - API Series #1

useful. I'm not sure. I think to be honest, I think this is more for learning how APIs work


Intro to APIs in Python - API Series #1

than anything else. It's pretty, I mean, it is pretty useful for this example. It's great.


Intro to APIs in Python - API Series #1

Now I want to show you something that's maybe more relevant. So I'm coming over to Google and


Intro to APIs in Python - API Series #1

we have the Google Maps API. So documentation here just describes how you access it. You can


Intro to APIs in Python - API Series #1

you can follow along if you want, but I'm going to very quickly just go through it. So we come down


Intro to APIs in Python - API Series #1

to create API keys, go to credentials page. You probably need to create a project. So we click


Intro to APIs in Python - API Series #1

that. It doesn't matter what we call the project. So I'm going to leave it as default name. We


Intro to APIs in Python - API Series #1

continue. Now you have to wait a minute for that to load. OK, and then this page will load over here.


Intro to APIs in Python - API Series #1

So what we want to do is scroll down and we want to use the GeoCoding API. So we click there,


Intro to APIs in Python - API Series #1

click enable. And once that is loaded, we want to head over here, go to credentials,


Intro to APIs in Python - API Series #1

credentials, create credentials API key, and we just copy this. OK, so I'm now just going to


Intro to APIs in Python - API Series #1

write that in here. So we've got API key copied over from before. So there we go.


Intro to APIs in Python - API Series #1

OK, I just need to make sure that is a code cell. OK, so that's the API key. And then we also need


Intro to APIs in Python - API Series #1

a few other things. So the API URL. So that is right out here. GeoCode in there. And then what


Intro to APIs in Python - API Series #1

we're going to do is we're going to enter an address. And with that address, we're going to


Intro to APIs in Python - API Series #1

return the latitude and longitude from Google Maps. So what we need to do is write requests


Intro to APIs in Python - API Series #1

dot get because we're getting that information. In here, we want our API address first,


Intro to APIs in Python - API Series #1

followed up with JSON like that. And then we want to include the address that we want to search for.


Intro to APIs in Python - API Series #1

So we go address equals. And then in here we write that. So I'm going to go with the address


Intro to APIs in Python - API Series #1

for the Colosseum in Rome. So Piazza del Colosseo. Like that. Now just note that here I've added the


Intro to APIs in Python - API Series #1

plus marks rather than a space. You can't use spaces in HTTP requests. So you can also use this.


Intro to APIs in Python - API Series #1

That represents a space. But I'm using the plus signs there. And then the last thing,


Intro to APIs in Python - API Series #1

actually let me show you what happens if I try and run it like this. So let's see. OK, we get a


Intro to APIs in Python - API Series #1

200. Interesting. Actually not expected. Let's see. OK, so we get 200 requests, which is strange.


Intro to APIs in Python - API Series #1

I would have expected, I would have expected like a not unauthorized response maybe. But fine.


Intro to APIs in Python - API Series #1

So it's just saying, so we return this error message saying you must use an API key. Because


Intro to APIs in Python - API Series #1

we have defined our API key up here, but we haven't included it in our request. So Google


Intro to APIs in Python - API Series #1

doesn't know we actually have one. So we also can include that. So we come over here.


Intro to APIs in Python - API Series #1

And key equals, and then we have API. OK. That's not what we, it's API key. OK. Now let's have a


Intro to APIs in Python - API Series #1

look at what we get. So we will check. We should still get a 200 response. It's good. And we also


Intro to APIs in Python - API Series #1

need to make sure that we write HTTPS there. Run that again. And then we get the, all of the address


Intro to APIs in Python - API Series #1

information for the Colosseum in Rome. And if we want, so we said we wanted the longitude and


Intro to APIs in Python - API Series #1

latitude. So how do we get that? We can have a look through it. So first thing we do, we can see


Intro to APIs in Python - API Series #1

everything in there. So we have results or status. OK. So let me copy this. Come down.


Intro to APIs in Python - API Series #1

So we have results and the results covers everything from here to,


Intro to APIs in Python - API Series #1

pretty far actually, to here. So our results is where we want to go for the longitude and latitude


Intro to APIs in Python - API Series #1

because we have the latitude and longitude here. So we go to results.


Intro to APIs in Python - API Series #1

And then in this case, it's a list. So we have all these different components here. And we


Intro to APIs in Python - API Series #1

want the first entry in that list. Like so. OK. So it's just a single entry in the list.


Intro to APIs in Python - API Series #1

I assume they do that if you are requesting multiple addresses. I'm not sure. And then we


Intro to APIs in Python - API Series #1

want to go into address components. OK. And then we're in another list again here. So we have


Intro to APIs in Python - API Series #1

this one or zero, one, two, three, four, five. So it's dictionary number five, I think. Oh, no, no, no.


Intro to APIs in Python - API Series #1

Oh, we don't have the coordinates. So let me remove that. It's not address.


Intro to APIs in Python - API Series #1

Components. So we just went into this list here. No, so we want geometry, I think.


Intro to APIs in Python - API Series #1

So let's go into geometry. OK. And then in here we have bounds. I mean, we can just actually,


Intro to APIs in Python - API Series #1

no, we can go for location. It's probably center. I mean, I'm not sure. I'm not sure.


Intro to APIs in Python - API Series #1

Probably the center. I mean, these are all obviously going to be very close. I don't think


Intro to APIs in Python - API Series #1

the address area for the coliseum is that big. So we go location. OK. And then here we have


Intro to APIs in Python - API Series #1

our coordinates. So we can just save those to chords like so. And then within that, we


Intro to APIs in Python - API Series #1

would just write chords. Lat I think it was. Latitude. And also chords long for the longitude.


Intro to APIs in Python - API Series #1

And there we are. We have our coordinates. So that's the Google Geocoding API.


Intro to APIs in Python - API Series #1

I like to use that example because the first sort of coding job I ever had was using this.


Intro to APIs in Python - API Series #1

Weirdly enough, although the code back then was absolutely horrific. I don't think I have it


Intro to APIs in Python - API Series #1

anymore. I'll have to have looked. But yeah, it was not very clean code. It's pretty horrific.


Intro to APIs in Python - API Series #1

And the next example. So we just, that was an example using the get request. But I want to


Intro to APIs in Python - API Series #1

show you also how we can use a put, post, or post put and delete as well. So I think the easiest


Intro to APIs in Python - API Series #1

easiest or most popular one is just get help. So we will come down here. So this is the


Intro to APIs in Python - API Series #1

documentation for getting access to it. Again, link in description. And what we do is we just


Intro to APIs in Python - API Series #1

come down here. We create a token. So I'm assuming you have a GitHub account. So you


Intro to APIs in Python - API Series #1

need to make sure everything is verified. And it says you need to go into GitHub and


Intro to APIs in Python - API Series #1

click on your profile, photo, click settings. So we'll do that. Okay, so I have it open here.


Intro to APIs in Python - API Series #1

Come over here, go to settings. And then over here on the left, we want to go down until we see


Intro to APIs in Python - API Series #1

developer settings over here. And we want to go to this personal access token. So click on there.


Intro to APIs in Python - API Series #1

And then we just need to click on this generating token. Again, we just want to click on repo. Yeah,


Intro to APIs in Python - API Series #1

so we need, if you also want to delete, I'm not going to do that because it's, I think,


Intro to APIs in Python - API Series #1

just a bad idea. You can click delete repo here as well. But I'll generate that token.


Intro to APIs in Python - API Series #1

And then I have it down here. So I'm going to copy that over into my notebook.


Intro to APIs in Python - API Series #1

Okay, so I've put that in a variable called GitHub key. And what I'm going to do here is


Intro to APIs in Python - API Series #1

create a new repository. So we're going to send a post request to initialize a new repository.


Intro to APIs in Python - API Series #1

To do that, we also need this other library. So import JSON. I'll show you why in a moment.


Intro to APIs in Python - API Series #1

So with JSON, we'll see in a moment, but I'm going to create this dictionary,


Intro to APIs in Python - API Series #1

which is going to contain information for my API requests. So typically, you would use this when


Intro to APIs in Python - API Series #1

you're using post put delete, think as well. So you generally you are going to use this,


Intro to APIs in Python - API Series #1

but not so much for get requests. So you have the name of the repository, which is going to be API


Intro to APIs in Python - API Series #1

test. And I'm also going to set to be public so we can see it. And I'm going to set up to true.


Intro to APIs in Python - API Series #1

Okay, and now I'm going to say request a post. And then we have the URL. So it is HTTPS.


Intro to APIs in Python - API Series #1

API.github.com slash user repos. And then in the header, so we have this other


Intro to APIs in Python - API Series #1

argument called headers. In here, we put sort of your authorization, typically and any other


Intro to APIs in Python - API Series #1

random little bits of information, which is what we're going to use it for. So we're going to use


Intro to APIs in Python - API Series #1

it to include our authorization code. And you typically find that that is formatted in slightly


Intro to APIs in Python - API Series #1

different ways. It's not just a string, you also need to include something else. So authorization.


Intro to APIs in Python - API Series #1

So this is just the parameter name. And then here, it's an f string. I need to put token.


Intro to APIs in Python - API Series #1

And then in here, I have my actual token. So GitHub key. Okay. And then this is where we include


Intro to APIs in Python - API Series #1

our payload and why we using the JSON package. So we write data equals JSON dot dumps. So this is


Intro to APIs in Python - API Series #1

essentially going to convert our dictionary here into a JSON formatted string. Because we can't


Intro to APIs in Python - API Series #1

just include a dictionary in JSON requests, you will get an error. So you do need to convert that


Intro to APIs in Python - API Series #1

into a JSON string. Okay, that looks pretty good. So let's try it.


Intro to APIs in Python - API Series #1

Let's see what we got. We got a 422 response. So let's see why.


Intro to APIs in Python - API Series #1

repository creation failed because I already have it. I've already tested it before apparently.


Intro to APIs in Python - API Series #1

So I'm not very creative with my names. So we're going to call it API test two.


Intro to APIs in Python - API Series #1

We go again. But notice with that response, we have encoded 422. We could see that was a 400 code.


Intro to APIs in Python - API Series #1

So we knew that something had gone wrong on our side. It's a client side error. Okay, so now,


Intro to APIs in Python - API Series #1

okay, looks good. Yeah, cool. So we have now created a new repository. So if I go to my GitHub


Intro to APIs in Python - API Series #1

and have a look, I should see that. So come over to my GitHub, go to repositories. And right at the


Intro to APIs in Python - API Series #1

top there, we see this API test two. So we're going to call it API test two. And we're going to


Intro to APIs in Python - API Series #1

call it API test two. And right at the top there, we see this API test two. So we did create it.


Intro to APIs in Python - API Series #1

And from now we can do other things. You can you can post things to your repos, delete them,


Intro to APIs in Python - API Series #1

you just need to modify which permissions you set when we build up when we create that personal


Intro to APIs in Python - API Series #1

API. So I think that's it for this video. I think we could quite a lot. So we've covered the


Intro to APIs in Python - API Series #1

essentials of APIs, had a quick look at the Pokemon API, then the Google Maps geocoding API, and now


Intro to APIs in Python - API Series #1

the GitHub API. So I think that's plenty. So in the next video, what I want to do is have a look


Intro to APIs in Python - API Series #1

at the Google Maps API with Flask and Python. So thank you very much for watching and I'll see you


Intro to APIs in Python - API Series #1


3 Traditional Methods for Similarity Search (Jaccard, w-shingling, Levenshtein)

Hi and welcome to this video. We're going to have a look at three different traditional


similarity search methods that we can use for comparing two separate chunks of text.


Those three methods, as you can see on the screen, we have Jaccard similarity, W-Shingling


and Levenstein distance. So let's jump straight into Jaccard. So Jaccard is actually super


simple. All we have is the intersection between two sets. So we have A and B up here. These


can be something like this. So we have, we'll just use numbers for now, one, two, three.


And let's say this is our set A. And we want to have a look at the intersection between


that and another set, which is set B. And maybe this is just two and four. Right. So


our intersection here are simply the shared items that both of these sets have. So we


just have one here. So the intersection would be one. And then we also have the union up


here. Now the union is still a set. So if we union all of these, we would get a total


of four characters because we have one, two, and then we have two over here again. So that


just creates one item. Then we have three and four. So all together that's four. We


divide one by four and we get a Jaccard similarity of 0.25. Now, what does that look like? So


it's like in Python. Let's have a look. So this is our implementation in Python. Now


it's pretty simple. All we have is we simply take our two sentences, X and Y, we convert


them into sets, and then we calculate intersection and union between them. And Python has built-in


methods for both of those operations for sets, which is pretty useful. So if we were to take


our previous example, so we had Jaccard and we need to pass strings here, it's fine. All


we're going to do is one, two, and three. And then here I'm just going to put two and


four. Same as we had before. We'll get 0.25, which is what we got before. So that is Jaccard


similarity. Let's move on to WShingling. Now, WShingling is very similar to Jaccard, but


rather than feeding in single words, we are feeding in ngrams. So an ngram is essentially


one or more words together. So for example, we replace n with one. That gives us a unigram.


And a unigram would just be what we did before, where we're just taking each word like that


as it is. So that would give us a list of single words. Now, if we move one up from


that, we can do two grams, which is a bigram. And a bigram is obviously taking two at a


time. So we would take these two words together. And then we would also take these two words


together and we would keep going through and taking two by two all the way until we get


to the end. And what we would get from that is a set where we have his thought. And then


next we would have thought process and so on and so on. So imagine we do the same as


we did with Jaccard similarity, but we do it with these ngrams. So bigrams, trigrams,


or even unigrams, that is W shingling. And if we were to write that out in Python, we


would get something like this. So we use this comprehension here. And what we're going to


do is iterate through each item in sentence A. So this one up here. We're splitting that


by space characters. And what I want to do is access each item in that list. So what


I'm going to do is put in range and we're going to use a length. So we're going to use


the indexing to get each item within the list. So for a, i, for i in that range. So that's


going to go through each item. So we run that and we get, well, we get each character there.


So I just need to make sure that we actually split the list before we do that. So we'll


just do here a.split. And that means I can also remove that. So now we have all the words.


What we want to do is get sets of two words at a time. So what we can do is we'll add


a list here and we'll do a to i plus one. So we get both of those together. And because


we're doing that, we also need to take a minus one at the end here so that we don't get an


index error running through the whole list with this plus one. And then we get two lists


of words. Now, the way that we want to implement it here, I mean, you don't have to do this,


but I think it looks a bit cleaner. Is we can use this join and then we put both of


those words together. And then at the end of that, we'll be taking a set. OK, and then


once we have that set, we just run that through this exact same function here. So we would


pass our new WShingling set to X and Y and calculate the Jaccard similarity between them.


And that would be WShingling. So that's why I said it's very similar because the actual


calculation itself is the same. It's just the approach that we take or the pre-processing


on our strings before we actually feed it into the calculation. Now, that's it for Jaccard


and WShingling. Let's move on to Levenshtein distance. Now, this is the formula for Levenshtein


distance. And I know it looks quite confusing or at least for me looking this first time,


it looks pretty complicated. If it doesn't, then that's great. But if it does look confusing,


don't worry, we're going to break it down and work through it. And it will seem really


simple once we're done. So this is the formula for Levenshtein distance. Now, an easy way


to understand what is going on is using this matrix here. Now, with this matrix, I've sort


of already filled in a few up here, but imagine they're not filled in because I'm going to


explain why in a minute. So with this matrix, what we're going to do is we're going to work


through each value or each cell within this matrix and calculate this formula for it.


So we start up here and at this point we have our two values. So we're going to put i here.


So these values or these rows are going to be our i indices. And here we're going to


have j. Now, at this point, j is equal to zero. And so is i. OK, because we are at cell


zero, zero. So we start there, we come down to here and we say, OK, if the minimum of


i or j is zero, then you need to pass over to this point here. And then this tells you


you need to take the maximum value between either i or j. Now, i and j, they're both


zero. So the maximum value between both of those is zero. So that means we remove this


x here. And that means that this value, so we pass that back here, the Levenstein distance


for this first item here is zero. So that's what we do. Now, at this point, we move on


to the next value. So we go this way. And then once we get to the end here, we move


on to the next row and keep going that way. And all of these bits I've filled in here,


they either have i equal to zero or they're going to be j on this column here is equal


to zero. So that means you pass it through here. And you take the maximum value of either


i or j. So, for example, if we let's do it here. We're just ignoring these two words


now. Let's explain how we or why those two words are relevant in a minute. At this point,


j is equal to seven. So j is equal to seven. Now, that's because we're on the seventh column


here. So we've got zero, one, two, and so on and so on until we get seven. So here,


j is equal to seven. So we have zero and seven. You pass that through down here, we get to


if the minimum of either of those is equal to zero, which it is because i is equal to


zero, we come over here. And we say, what's the maximum value of either of those? It's


seven. So we remove all of this. And that means that this value here is equal to seven.


Now, that's easy enough. But what happens when we start getting into these values here?


So this is where things change a little bit. Now, with Levenstein's Distance, what we're


essentially doing is taking these two words and saying, what is the minimum or optimum


number of operations for us to convert one word into another word? And when we look at


this equation down here, we actually see three alternative operations. And that is deletion.


So here, delete. So this maybe we have, for example, here, i, and over here, we have e.


If we wanted to convert Levenstein into this Levensten at the top, we would probably want


to delete this i because then we would get at the end, TEN, which matches up to what


we have up here. So we would want to delete that character. And so we would process it


through that delete operation. Below that, we have the insert operation. So this is inserting


a new character. So maybe we're going the other way. Maybe we were here at N and we


were comparing against i and we saw, OK, the best way for us to do this is to insert a


new character, which would be i. And then here is the substitution operation. And this


is where you're swapping one character for another character. So in the example here,


going from e to i up here, we might want to substitute that e for an i. So there are the


two or the three operations that we have there. And whether you want to remember that or not,


it's not really that important because we can implement this and we don't really need


to think too much about what each of those operations is actually doing. So let's just


remove those for now and let's work through an example. So we have this first example


here at j equals one and i equals one. So we have one and one. Now we come down here


and we have this. OK, is the minimum of i or j equal to zero? No, they're both one.


So that means we have to go to the else here and then we pass through this. So what we


are saying here is what is the minimum value of all of these? So what is the Levenstein


value that we already calculated at i minus one j? So i minus one j is at position zero


one, because it's one minus one for the i and just one for the j. So what do we have


there? So zero one. So i zero and j one. Here we have this one value because at this point


we've already calculated because we go in that order. We go start from the top and go


all the way right onto the next row and so on. So we always have these numbers calculated


by the time we get to comparing the minimum value. So we have zero and one there and the


number is one. So we let's remove that and we say, OK, this is equal to one. What's the


next part? We have i and j minus one. So the position one, position one and j minus one,


which is zero. So what do we have there? So we have here and here. We again have another


one. OK, so we say that's equal to one. Then we come to this final one here. i minus one


j minus one. OK, so that means zero zero. So position zero zero. What do we have there?


OK, we have this number zero. There we go. So we take the minimum value of those, which


is obviously this zero value here. And we have so this is going to be equal to zero.


And then we have this here. And what this part of the formula is telling us is we need


to add one if ai is not equal to bi. So up here, something I didn't explain, but a is


this word and b is the other word. So it's ai we have l and at jb we have l. Are both


of those the same? Yes, they are. So that means we don't need to add the one. OK, so


then that means that our value here is equal to zero. And this this looks like a zero.


I didn't mean for it to. It's actually supposed to be a circle. So this is a zero. Now let's


remove all that for now. Let's do one more. So let's try and make this one quick. So here


we have i is equal to one, j is equal to two. We come down. We know that they are neither


those are zero. We come to here, come to here. OK, what is at position zero, two, zero, two?


We have two. So this is equal to two. At position one, one, what do we have? So one, one. So


this is a zero. And then we have position i minus one, j minus one. So that is i equals


zero and j equals one, which is this one here. So we have one. What's the minimum value of


all those? It's zero. And are a i and b j the same here? Well, a i is our L again. And


b j is an i this time. This is i up here. So they're not the same. So this time we do


one plus zero. So then up here we put a one. And then we would just keep doing that for


every single value going in this direction, go to the end and then we'd move on to the


next row going in this direction again until we get all the way to the end. By the time


we get to the end, it's going to look something like this. And what we do when we get this


is we look at the value that we see in the bottom right corner, which is here. Or if


you're in Python, we would get that value by indexing at minus one, minus one. And that


is our Levenstein value. And you can see this as almost like the optimal path through our


matrix. So we're just following this line all the way over here. Here we had to move


a little bit and then we find our optimum number of operations is three. So there's


three deletions, insertions or substitutions needed to go from our initial word over to


our other word. Now, how does that work in Python? Well, in Python, it looked like this.


So at the start here, we add a additional character. So if you remember from the matrix,


you see we had this first initial part here. These are just empty characters. And we replicate


that in our code by adding these spaces here. And then after that, what we do is initialize


a empty array. So that is our first stage that we saw before. So we have the empty array.


It's just going to be zeros at this point. And then we begin iterating through each value


and finding the best path. So we have i and j here, as we saw before, exactly the same.


So we have i which corresponds to our word or sentence a, and j which corresponds to


our word or sentence b. And then we say, if the minimum of i or j is equal to zero, then


we calculate that position as the maximum of i or j. So exactly the same as what we


covered before with that matrix and the formula. So that is the top line here. So this bit


followed by this. So the exact same. We're saying if the minimum, then choose the maximum.


And then in our logic, we move on to the else. So if the minimum of i or j is not equal to


zero, just like we did in that formula, we do this. So we say we calculate those three


rows that we had. So those three possible operations with deletion, insertion or substitution.


And then we take the minimum value of all of those. So the best path. And then if our


two current characters don't match, we add one. So again, we saw that in the formula


before. Right here. So we're saying if the characters that a and b don't match, add one.


And if so, we do the same. So we run through, we do that for every position in our matrix.


And then what we do down here is return. I return matrix. And then I also return the


actual loan sign distance from the final point in that matrix. So the bottom right value.


So we have that formula. Let's see how it does on our example. So we have these two


words. I think this should be a h. Let's have a look. Yep. So we need that to be a h. So


we're going to run both of those through our formula here. Let's have a look at what we


get. Okay. So run all the way through so I can see here. So this is supposed to be an


i 11 h 10. Okay. And then we get this value. So we get in our matrix, see that the final


value is at three. And then we also pull that out here as well. So those are our three traditional


similarity search techniques and how we can use them in language. Now all of them are,


I think they're all very popular with good reason. And it's definitely worth being able


to use them. Now the one place where these sort of struggle is where we are maybe looking


at more semantical meaning. So for example, if I were to say the words hi and hello, although


they have the same meaning, none of these techniques would be able to identify those


two words as being similar or matching. And in that case, we would kind of need to move


into the more vector based similarity search methods. And in particular where we start


using dense vectors for representing those words. But for simple comparisons where we're


comparing the syntax between two sentences or words, these traditional approaches are


incredibly useful and incredibly powerful. But that's it for this video. So I hope you've


New Features in Python 3.10

Hi, welcome to this video.


New Features in Python 3.10

We're going to explore what is new in Python 3.10.


New Features in Python 3.10

There are a few different things I want to cover there.


New Features in Python 3.10

We have some more additions to typing, which I think are pretty cool.


New Features in Python 3.10

The ParenthesizeContextManager, which is kind of an interesting update


New Features in Python 3.10

because it's not really an update.


New Features in Python 3.10

So we'll get into that pretty soon.


New Features in Python 3.10

We have Structural Pattern Matching, which I've covered before.


New Features in Python 3.10

We're just going to run through that really quickly in this video.


New Features in Python 3.10

And then there is also a load of new error messages,


New Features in Python 3.10

which are pretty interesting, actually.


New Features in Python 3.10

There's some really good ones in there.


New Features in Python 3.10

So we'll go through all of those new features in Python 3.10.


New Features in Python 3.10

Okay, so in the last few iterations of Python,


New Features in Python 3.10

there's been a steady improvement of the Python's


New Features in Python 3.10

type hinting or type annotations capabilities.


New Features in Python 3.10

And with Python 3.10, we are seeing more of that again.


New Features in Python 3.10

Now, there's a few changes or a few updates and features


New Features in Python 3.10

with the typing in Python 3.10.


New Features in Python 3.10

But I think the most interesting one is the addition of a new operator.


New Features in Python 3.10

So this is what we used to have to do in Python 3.9 and before.


New Features in Python 3.10

So in here, I'm just defining really simple function


New Features in Python 3.10

that adds two integers or floats together.


New Features in Python 3.10

Now, if we want to define x and y as being potentially


New Features in Python 3.10

an integer data type or a float data type,


New Features in Python 3.10

then we would have to use this union operator,


New Features in Python 3.10

which would import from typing up here.


New Features in Python 3.10

Now with Python 3.10, we don't have to do that anymore.


New Features in Python 3.10

It's much simpler.


New Features in Python 3.10

All we do is include the pipe character here,


New Features in Python 3.10

which is our new union operator,


New Features in Python 3.10

which obviously it looks a lot cleaner.


New Features in Python 3.10

So that's the main new feature with type annotations in 3.10.


New Features in Python 3.10

So let's move on to the new parenthesized context manager.


New Features in Python 3.10

Okay, so what we're talking about when we talk about context managers


New Features in Python 3.10

is exactly what you can see on the screen now.


New Features in Python 3.10

So we have this is the most common example


New Features in Python 3.10

of the context manager in Python.


New Features in Python 3.10

And this is just how we read in our files.


New Features in Python 3.10

So we have with, so this is setting up our context.


New Features in Python 3.10

And here we are assigning this stream here as f.


New Features in Python 3.10

Okay, and then within this indented area, we can read f,


New Features in Python 3.10

but outside of it, we can't.


New Features in Python 3.10

So we couldn't write something like text equals f.read.


New Features in Python 3.10

Because in this case, f just doesn't exist outside of that context.


New Features in Python 3.10

So that's what we mean by context manager.


New Features in Python 3.10

Now, if we wanted to open multiple context managers within the same area,


New Features in Python 3.10

we could write something like this.


New Features in Python 3.10

So we'd have with open, then we have another open afterwards,


New Features in Python 3.10

and then we would be able to access both of those within this indented area.


New Features in Python 3.10

The only problem with this is that we're not able to do that


New Features in Python 3.10

because we're not able to access both of those within the same area.


New Features in Python 3.10

So we're not able to do that.


New Features in Python 3.10

The only problem is that we would have to keep all of this on the same line


New Features in Python 3.10

due to limitations with Python's previous parser.


New Features in Python 3.10

Now, the parser got updated in Python 3.9,


New Features in Python 3.10

which is why I say this is kind of not really a new update,


New Features in Python 3.10

but it is a new update.


New Features in Python 3.10

So before then, we had to keep everything on a single line.


New Features in Python 3.10

So that's how it works as well.


New Features in Python 3.10

But the problem with this is that it's not technically Pythonic,


New Features in Python 3.10

which, I mean, for most people, it doesn't really matter.


New Features in Python 3.10

But now what has been added, thanks to the new Python parser,


New Features in Python 3.10

which introduced in 3.9,


New Features in Python 3.10

is we can include parenthesized context managers.


New Features in Python 3.10

So we can add these inside brackets.


New Features in Python 3.10

And that will now work.


New Features in Python 3.10

This is officially a new feature for Python 3.10.


New Features in Python 3.10

But we can actually switch back to Python 3.9.


New Features in Python 3.10

And if I run this again, you'll see that it still works.


New Features in Python 3.10

So that's kind of weird, right?


New Features in Python 3.10

Now, the reason for that is that the new parser introducing at 3.9


New Features in Python 3.10

allows this syntax to work,


New Features in Python 3.10

but it was not added as a genuine new feature until Python 3.10.


New Features in Python 3.10

Now, why that has been done, I really can't tell you,


New Features in Python 3.10

because I don't understand.


New Features in Python 3.10

But that is just how it is.


New Features in Python 3.10

Now, if we take this back one version further to 3.8,


New Features in Python 3.10

we see that we do actually get the invalid syntax.


New Features in Python 3.10

So this is a new feature, but it's also not a new feature.


New Features in Python 3.10

So yeah, that's a strange one.


New Features in Python 3.10

Now, the next one, which I think is probably the most hyped new feature of 3.10,


New Features in Python 3.10

and with good reason, it is very cool, is the structural pattern matching.


New Features in Python 3.10

Now, we can write this statement here.


New Features in Python 3.10

And this is just a simple example.


New Features in Python 3.10

We're going to move on to a real example in a moment.


New Features in Python 3.10

This is an if else statement, and we're just checking this code.


New Features in Python 3.10

We can run that without a problem.


New Features in Python 3.10

And some new syntax has been added in Python 3.10.


New Features in Python 3.10

And that is the match case statement.


New Features in Python 3.10

So this is the equivalent of what we did up here,


New Features in Python 3.10

but using the new match case syntax, which, okay, that's, you know,


New Features in Python 3.10

it's interesting, it's cool, but it doesn't really bring us anything new.


New Features in Python 3.10

We could just do that with a if else statement before.


New Features in Python 3.10

So that's not particularly interesting.


New Features in Python 3.10

What is interesting is the structural pattern matching part of this new syntax,


New Features in Python 3.10

which allows us to do this match case comparison.


New Features in Python 3.10

But rather than specifying whether that HTTP code is an exact match to whatever values are


New Features in Python 3.10

in those case statements, we can check the structure of a object


New Features in Python 3.10

and check if it matches a pattern which we pass through the case statement,


New Features in Python 3.10

which is very, very cool.


New Features in Python 3.10

Now let's see how that works.


New Features in Python 3.10

So if I remove these two, I'm going to define a dictionary here.


New Features in Python 3.10

So dictionary A and dictionary B.


New Features in Python 3.10

Now these two both contain similar information, but they're in a different structure.


New Features in Python 3.10

So that means that we would have to access both of these using different paths.


New Features in Python 3.10

Now what we can do is create a pattern that matches both of these


New Features in Python 3.10

and add them both within a match case statement.


New Features in Python 3.10

And we can use that to check which pattern we have


New Features in Python 3.10

and perform a different operation on each one.


New Features in Python 3.10

So to do that first, so this is our code.


New Features in Python 3.10

We're going through in a loop through dictionary A, dictionary B,


New Features in Python 3.10

and I've just added this string called test-based.


New Features in Python 3.10

And I've just added this string called test, that shouldn't match to anything


New Features in Python 3.10

because it's just a string.


New Features in Python 3.10

Okay, so we're looping through each of those


New Features in Python 3.10

and we're going to test the match case on each one.


New Features in Python 3.10

So first, so we're matching here and then we have our case.


New Features in Python 3.10

And then here is our pattern.


New Features in Python 3.10

So this is a pattern we want to match.


New Features in Python 3.10

Now if we compare that pattern, we'll see that we have ID and meta,


New Features in Python 3.10

the ID and meta and inside meta,


New Features in Python 3.10

we have another dictionary which contains source and location.


New Features in Python 3.10

And here we have source and location as well.


New Features in Python 3.10

Now this doesn't need to be perfect match, by the way.


New Features in Python 3.10

We could, this would also match if I got rid of location like that.


New Features in Python 3.10

And obviously we'd have to go in loc here as well.


New Features in Python 3.10

So it doesn't have to be a perfect match,


New Features in Python 3.10

just as long as whatever you have entered


New Features in Python 3.10

does fit into the structure that we are comparing against.


New Features in Python 3.10

Now that's the first one, but that will not match to this


New Features in Python 3.10

because we don't have the meta key


New Features in Python 3.10

and the meta does not contain no dictionary.


New Features in Python 3.10

Okay, in dict b.


New Features in Python 3.10

But it does match this one, which is ID, source and location.


New Features in Python 3.10

Same as this one here.


New Features in Python 3.10

So that will match.


New Features in Python 3.10

And then here we have our catch all statement.


New Features in Python 3.10

So this is like our else statement.


New Features in Python 3.10

And this will just catch any items


New Features in Python 3.10

that do not match either of those cases,


New Features in Python 3.10

e.g. our test at the end there.


New Features in Python 3.10

So let's run that and see what it prints.


New Features in Python 3.10

Okay, so we see that we get the first one.


New Features in Python 3.10

So it manages to extract, ident, source and loc


New Features in Python 3.10

and print those all out.


New Features in Python 3.10

And then for the second one, it does the same,


New Features in Python 3.10

even though they're a different structure


New Features in Python 3.10

because it matches to this different case.


New Features in Python 3.10

And then here we have our catch all,


New Features in Python 3.10

and we print out no match.


New Features in Python 3.10

So this, I think easily is the coolest feature in 3.10.


New Features in Python 3.10

And I've already found it useful for processing data


New Features in Python 3.10

in a similar way to what I'm showing here.


New Features in Python 3.10

So it's, in my opinion, super cool


New Features in Python 3.10

and something to be excited for.


New Features in Python 3.10

So let's move on to our final new feature,


New Features in Python 3.10

which are the new and improved error messages.


New Features in Python 3.10

Okay, so let's move on to the next feature.


New Features in Python 3.10

Let's move on to the new error message in 3.10.


New Features in Python 3.10

So they've done quite a few new messages here


New Features in Python 3.10

that are just a lot easier to understand.


New Features in Python 3.10

So for example, if we take this unclosed dictionary,


New Features in Python 3.10

in 3.9, we get this unexpected EOF while parsing.


New Features in Python 3.10

EOF means end of file.


New Features in Python 3.10

So this basically is telling us that the parser


New Features in Python 3.10

has gone all the way to the end of the file


New Features in Python 3.10

and it hasn't found the closing bracket of our dictionary.


New Features in Python 3.10

And okay, fair enough.


New Features in Python 3.10

Once you've been using Python for a while,


New Features in Python 3.10

you know what it means, that's fine.


New Features in Python 3.10

But this isn't particularly clear,


New Features in Python 3.10

especially if you're new to Python.


New Features in Python 3.10

I mean, I'm certain that the first time I got this error,


New Features in Python 3.10

and I'm certain that almost everyone,


New Features in Python 3.10

the first time I got this error,


New Features in Python 3.10

will have had to Google what this means.


New Features in Python 3.10

Now in Python 3.10, now it's really simple.


New Features in Python 3.10

It's telling us that our dictionary was never closed,


New Features in Python 3.10

which is much better.


New Features in Python 3.10

Now in the same sort of area,


New Features in Python 3.10

we also have unclosed strings where we get this EOL,


New Features in Python 3.10

which is end of line.


New Features in Python 3.10

And that means the same thing again,


New Features in Python 3.10

saying we've got to the end of the line


New Features in Python 3.10

and your string isn't closed.


New Features in Python 3.10

Now in Python 3.10, rather than that,


New Features in Python 3.10

we get this unterminated string literal.


New Features in Python 3.10

And that's just so much easier to understand.


New Features in Python 3.10

Another one is assignment versus comparisons.


New Features in Python 3.10

So obviously here we should have a double equal assignment


New Features in Python 3.10

because we're comparing, we're not making two equal to three.


New Features in Python 3.10

So if we run that, we just get invalid syntax.


New Features in Python 3.10

Now for us, obviously, we know what's going on there.


New Features in Python 3.10

But maybe if you're new to the language,


New Features in Python 3.10

it might not be so clear.


New Features in Python 3.10

So now what we get is cannot assign to a literal here.


New Features in Python 3.10

Maybe you meant double equal sign, not a single equal sign.


New Features in Python 3.10

So a lot clearer again.


New Features in Python 3.10

Now, another area that I think is pretty interesting,


New Features in Python 3.10

although I can't seem to get working at the moment,


New Features in Python 3.10

is where we enter a incorrect attribute name.


New Features in Python 3.10

So usually we get this attribute error.


New Features in Python 3.10

And that is essentially what we get


New Features in Python 3.10

over here in Python 3.10.


New Features in Python 3.10

But as well as this, although we don't see it here,


New Features in Python 3.10

we should also get a suggested attribute name


New Features in Python 3.10

based on whichever attribute has the closest match.


New Features in Python 3.10

Now, I'm not sure why it doesn't work here.


New Features in Python 3.10

But if we take a look here on the actual


New Features in Python 3.10

What's New Python page, we can see that


New Features in Python 3.10

this should give us a recommendation


New Features in Python 3.10

of do you mean named tuple, which I think is pretty cool.


New Features in Python 3.10

And we should also get the same with named errors as well.


New Features in Python 3.10

So if we have this new variable on the left,


New Features in Python 3.10

we'll see that we get this name error.


New Features in Python 3.10

New vr is not defined because we have typo.


New Features in Python 3.10

It's fine.


New Features in Python 3.10

And then in Python 3.10, what we should find


New Features in Python 3.10

is that this will also recommend,


New Features in Python 3.10

hey, this isn't quite right.


New Features in Python 3.10

Maybe you meant new var.


New Features in Python 3.10

Okay.


New Features in Python 3.10

And we can see this in this example here


New Features in Python 3.10

on the What's New page.


New Features in Python 3.10

So overall, I think those new error measures


New Features in Python 3.10

are definitely a lot clearer.


New Features in Python 3.10

And they're probably going to help a lot of people.


New Features in Python 3.10

So that's really good to see.


New Features in Python 3.10

Okay, so that's everything for this video.


New Features in Python 3.10


Novice to Advanced RegEx in Less-than 30 Minutes + Python

Hi and welcome to the video. Today we're going to look at regex which is short for regular expressions.


This is essentially the de facto standard for parsing text. So what we're going to do in this


video is run through the basics of regex first. So stuff like how to define digits, whitespace,


how to use quantifiers to tell us how many digits or whitespace or any other character we want to


include, how to use capture groups and character classes, and also how we can use boundary


definitions. So how we define the start of a line, the end of a line, or boundaries of a word.


And we should move through these quite quickly because they're not very difficult. They're


pretty straightforward. And then we can move on to what I think is the more exciting, interesting


stuff which is a little more advanced. So these are things like look ahead or look behind search


modifiers and conditioners which are essentially like if else statements but for your regex code


which is pretty interesting. So we're going to work through all of that in this video.


We're going to code through a few examples in Python. We're also going to use regex 101 which


is like an interactive debugger or regex building tool that we can use. So it should be pretty


interesting and let's jump straight into it. Okay so we're going to start in regex 101 and we're


just going to have a look through a few of the meta characters. So let's say we have this string


I'm just going to make it up. We have a few letters in here, some numbers, and two other


characters as well. Now we can obviously directly match these by actually writing out the exact


characters but generally we're not going to want to do that if we're using regex.


So this is where we start to use meta characters. So we'll just go through the most common ones. We


have digits. This will match any number so anything between 0 and 9 and we can inverse


that with a uppercase D and then that will match anything that is not a digit.


We have w which will match any word character and then we can also reverse that with a letter.


And then we can also reverse that again by capitalizing it. So all of these


meta characters we can usually uppercase them and it will reverse what they're doing.


Do white space with s. In this case we don't actually have any white space so let's add some in


and it will also match new lines as well. So you can't see this highlighted but if you look up


here you can say two matches then if I add that new line in it goes up to three. Now we'll do


anything but white space we just uppercase that again and then this one's a little bit of a


special one. This one matches any character except from new lines so this is not matching our new line


here. We obviously can't uppercase this one but I suppose the opposite would actually just be a new


line character like this. So let's switch over to Python and see how we would do this. So we import


re which is the Regex module and we'll go through these a little bit in more depth later but for


now I'm just going to do re.findall. And then our first argument here we put the pattern that we are


going to use to search. So in this case it would be backslash n although we're not going to use


that we're going to use backslash d for any digits and then let's just pull this one in.


Okay and we return one zero zero zero okay so these four characters here which is exactly


what we would get here. Okay so that's cool. Now in the case of this full stop what if we would


like to actually match a full stop and just a full stop. To do that we actually escape the


meta character using a backslash just like that. Okay so that's it for meta characters let's move


on to quantifiers. So quantifiers essentially allow us to match a specific number of characters.


So as of yet we've only been matching one at a time. So here we are matching four characters but


we're only matching one character at a time four times one two three four whereas quantifiers


allow us to write our pattern and then add this quantifier to specify how many times such you


match that. So the first of those is the one or more quantifier so this will match that pattern


one or more times just a plus sign. We also have zero or more quantifier so this is matching that


pattern zero times or more times. Okay and something that we can also add in here as you


will see this is matching it as many times as possible but maybe we actually want to limit


the number of times I'm matching something and this is a difference between what is called greedy


and lazy quantifiers. So at the moment we have a greedy quantifier so it's saying one or more


times and it's going all the way up to four. Okay which is as many characters as it can fit into


its pattern but if you want it to not do that and instead be lazy and simply pick up as few


characters as possible that match the criteria we can just add a question mark onto the end


and then we're back to matching just one because it's one or more and we are limiting it to the


minimum number of matches there. So keep that in mind and we'll just quickly go over again


towards the end just a little bit. We also have the once or none so let's write a new test string here.


So here we have a few words and we'd like to match all of the words so what we can do


is this and here we're kind of matching all the words but there's this one in the middle where we


have a hyphen in the middle and this is something that will happen quite often and ideally we also


want to put good-hearted as a single word so we could do this


we could do this but then we're only matching that single good-hearted part


so instead we add a once or none quantifier. Okay so now we're matching that word as well.


Now if we also want to match the a because you can see here it's not matching because


we're expecting at least two word characters because we have w here and w here we can just add


a zero or non quantifier onto the end there and now we're getting all of our words together


including the hyphenated words. We can also specify a specific quantity which we do like this


so here we are getting three word characters at a time you can see here we're not specifying


three characters that make up a word we're just saying three characters so here we're getting


multiple matches for single words which is fine because we haven't specified that we'll


go over how to define word boundaries later and we can also turn this into a range so let's go


three to five. Okay so now we're matching at a minimum three characters and a maximum five.


Now you might have guessed this but we can actually just remove one of these numbers to get


less than five. Now if this doesn't work for you just make sure that you are using the Python


flavor because for other languages this might change and if you're on PCRE for example this


won't work so change back to Python and we can also do three or more as well. Now I think this


is a good example with our lazy quantifier so here we're matching between either three up to


five characters at a time if we had a lazy quantifier it's always going to limit that as


much as possible so we're going to go down to three. Okay so you can see here that it's limiting


how many characters it's including in there it's getting lazy rather than greedy. Okay so let's


write out a new example here. Okay so a few unexpected words are to be expected so this is


a good example of where we can use capture groups. So anything contained within round brackets


will create a capture group. So a capture group is simply a fancy way of saying treat everything


within these brackets as a single unit. So you can see here I only put these dots in as a filler


but it actually matches because those dots mean anything. So three anythings in a row and three


things in a row and this is matching you know basically anything so we have all these matches


here and it's treating those as a unit so it's doing three anythings and matching that and then


moving on to next three anythings. Now what we can do is we want to match unexpected and expected so


we want to match the word with or without its negative prefix. So we can add expected here


but here we're only getting expected we're not getting the un from unexpected. So we just add


this so now we're getting unexpected but we're not getting expected because we have specified okay


we want un here we want this capture group. So all we need to do is actually make this optional by


adding a zero or one quantifier and there we go we are now capturing unexpected and expected.


So let's have a quick go at this in python see what it looks like.


Okay and we run this and now we find that we are only seeing un which is probably not what


we're expecting. The reason this happens is that find all tries to match capture groups


which is exactly what we have here. So what we can do is modify this capture group to make it a


non-capturing group while still maintaining this behavior of zero or one. So all we do to do that


is add a question mark inside followed by a colon and then here we are capturing everything again.


So that's just a little bit of a strange behavior to watch out for. Now we can also add a or logic


to our capture groups. So maybe we want to capture anything where we are saying expected


with a negative prefix and that can either be not expected or unexpected.


And we want both of these to match.


Now to do this we actually just add a pipe into our capturing group and then we add not like so.


And now we are matching both non-expected and unexpected. Okay so that's it for capture groups


and let's move on to character sets. So the syntax for character sets is kind of similar


to the syntax for capture groups in that we use brackets but this time they're square brackets


instead. And you can see these kind of like a list so anything we put within here will be treated as


a character to match. But unlike capture groups it's not treating them all as a unit. So if we put


on in here it's actually just matching either u or n and we could put unexpected and it's not going


to match unexpected as a unit it's just going to match each one of those words within those


square brackets. So let's return to our earlier example. So earlier on we were matching all of


the digits in our string. So what we could do is write out all of the digits like this and we get


the exact same effect. Obviously this is quite long so what we can do instead is write this with


dash in the middle and this is any character within the range of 0 to 9. We can also add


letters to this so a to z for example and you might also think okay we can also add these hyphens


in right. But obviously we are using these hyphens to define our ranges. So in order to add a hyphen


in here we need to use backspace to escape it and now we are matching the full string. And if we want


to match full string as a whole of course we just add our quantifier. Now let's move on to boundaries.


So I'm just going to write out a new string for this. Okay so here I want to show you the start


string and end of string boundaries. So the start string is using this character. So here if we put


if it it's only going to match if it at a start here. It's not going to match if it's here as well.


And if we remove the character it does. Okay so we add this character to specify that we only want


to search from the start very start of our string. Now the equal and opposite of the start string


character is the end string character and that is a dollar symbol. So let's rewrite this.


Okay and we want to look for example.


And here with the dollar symbol we only match the final example rather than both of them. So you can


see there. I'm going to go back to one of the earlier examples again now.


Okay so here I also want to show you the word boundary. So the best way to identify word


boundary is not by using no for example white space because yes that does work in a lot of cases


but it doesn't work if we have a comma, full stop, hyphen or anything like this. So what we can do


instead is use backslash b and this identifies every single word boundary within our text as


you can see from the pink lines. So then we can use that to capture any of our words and now quite


easily we've captured every single word and we're pulling them out in a more efficient way than if


we had tried to write no s or if we'd have gone with a grouping like this and added all these


different things. All we need to do is add a word boundary. Okay so now we'll move on to some of the


what I think are the more interesting and definitely a bit more advanced methods in


Regex. So the first of those is the look ahead and look behind assertions. So if we have this string


here we have two hello worlds one of them is preceded by a one and colon the other preceded by


two and a colon. Now what if we want to match hello world but we only want to match hello world


if it is preceded by a one and a colon but we don't want to include that one and a colon within


our pattern because if we want to do that we would just write this but this will return


the entire string. So I'll show you over here. Okay so we're returning the full string. What if we only


actually want to pull out this hello world? Well we could go for hello world but then we're returning


both. We don't want to do that we only want the first one. So to do this we use a look behind


assertion. So this means that we are looking behind our pattern which means anything preceding it and


we are asserting that there is this other pattern there. So we do this with this pattern here.


Okay and anything we place in between this equal sign and this closing bracket is included within


our assertion pattern. Okay so now we are matching just this first hello world. So if we go and take


this and put it into our code here we will return just the first hello world. Now on the other hand


maybe we want to match something that comes after our pattern and to do that we use a look ahead


assertion which as you've probably guessed is basically exactly the same but on the other side.


It does use a slightly different syntax but other than that there's really no different. So in our


case we're going to search for this comma. So in between the equal sign and the closing bracket


here that's where you put our pattern and here again we're matching this hello world.


And we can put this in python and of course we'll just get the exact same thing. Now on the other hand


maybe we don't want to assert that something is in front or behind our pattern we actually want to


assert that something is not there. So what we can do is we can make this a negative look ahead by


replacing this equal sign with an exclamation mark. And now we are looking for the hello world that is


not followed by a comma which is obviously this one. And again with the look behind we just modify


that as well. So whereas the look behind looked like this we just again remove the equal sign and


replace it with a exclamation mark and then we get the second hello world. Okay so that's it for the


assertions let's move on to modifiers. So we can actually see we have a few modifiers here and


these are essentially ways of modifying the behavior of our entire regular expression. Now


obviously python doesn't have this little set regs options here so what we do instead is we can


either do an inline modifier like this one and let me just give you a good example quickly.


So we'll just write out a string that includes a newline character in the middle.


Okay and we're going to match character and then anything following that character. Now if you


remember this anything meta character does not actually match anything it matches anything except


from newlines. So in this case it is not matching here because we are expecting newline. Now if we


open this we can see that we have this single line dot matches newline. So we add that and now it's


changed the behavior of our reject and the anything meta character also matches newline characters.


Now let's remove it from here and we can add it inline like this.


So here we're just adding the s within this global modifying function and we can also add


other modifiers as well if you want so you just all you need to do is add the letter that represents


that global modifier and add it within those brackets. Now if you look at the


function itself now if we take that over to python


and we add this in so here is our inline modifier yep it works that's great if we get rid of this


it doesn't match anymore and that's what we would expect. Now in python we can also add


modifiers within the function itself so what we do is we add re dot and then the capital of the


modifying flag and there we go it matches again. So there's a few different ways that you can do it


in python. Now let's move over to conditionals this is the last one we're going to work through


and these are probably a little more complex in my opinion to actually read and understand.


So I'm going to use a few different examples here.


I'm just going to quickly make this up.


Okay so we have these three lines each of them has something in common. Now what I want to do


is enter a condition within a capture group which is either true or not. Now I don't really want to


specify that I need this condition within my reg ex because if it isn't there I want to search for


something else and to do that we add this group here and this is basically our if else group.


So I'm just going to put i here for now I'll explain that in a moment and this is our if the


condition is true then we also want to search for this. If the condition is not true then just search


for this instead. So our condition here is within a capturing group and this token here refers to


the index of our capture group. So in our case we only have one capture group and you can see here


even highlights first capturing group. So this actually needs to be one and then that essentially


links our condition within this capturing group to this if else statement and we can see that now


okay this condition is not true because we haven't written condition anywhere. So it's going into the


if clause and it's saying okay if it's true which it isn't search for this right but it isn't true.


So we are actually only searching for buy so it is producing a match because all we need to match


to is buy but in reality we actually do want to search for a condition which is going to be hello


and here now we are matching two things. I'm matching this because hello is true here and if


hello is true our if else statement says okay now we need to match space world which we do right here.


And on this line just like before we're finding okay hello is not true doesn't say hello here.


So this is not what we want to look for we actually want to look for this which is our else statement


and we do in fact have buy here so that again does match. So I mean that's everything on the


regular expressions and I just want to quickly go through what the difference is between re match


search and find all in python. So let's remove these. So I'm just going to write a string very quickly.


Okay we're just going to use this as our example. Now if we do re match you remember before we had


this sort of string character re.match essentially is like putting this character in front of whatever


you type within it. So what I mean by that is if we do re.match hello


we will get a match. So we also as well we need to put dot group after we use re match or re search


something to be aware of. Okay and yeah okay fine we would expect that because we're searching


through and yet there's a hello here there's a hello here of course it's going to match a hello


that's fine but if we put world we don't match anything. Okay so it's a non-type which means


nothing has been matched and the reason for that is that match automatically adds a sort of string


token in there. So if we put world at the start here this would work okay but obviously before


we didn't have it there so it didn't work. So that's what re match does it also just returns


you one match unlike find or you remember we were getting a list of matches. Now re.search


doesn't specify that we only need to look at the first part of the string instead re.search


looks through the whole thing. So if we search for world we actually do get a response because


we're not specifying it needs to be here. Okay so that's great but you'll also notice that we are


only returning one thing here and that will not change if we add hello okay we're still just


returning one item. So what re.search does is it comes through here it searches a whole string but


it only searches for the first instance so it gets to here it says okay I found hello and then it


returns that match it doesn't go any further and finds anything else and that's where find all is


a little bit different. So find all we can't use group here we just print out x does go through and


find everything. So that's it for this video I hope it's been useful we've been you know through


a lot of regex so don't worry if you can't believe your mind a bit if you're new to this


it is quite a lot but nonetheless regex is super important it's I would definitely recommend


getting familiar with it if this is new to you it's it's an incredibly useful skill no matter


what you are specializing in as long as you code you're probably going to use regex so that's the


Sequential Model - TensorFlow Essentials #1

Hi, welcome to this introduction to the sequential model in TensorFlow.


So we're just going to go ahead and start a new notebook here.


Now what we want to do is import TensorFlow.


Now the sequential model is one of two different approaches that we can use for building our


models in TensorFlow. Those are the functional API and then the sequential model that we're


going to go through here. So the sequential model is better for simple sequential stacks of layers


where each layer has just one input and just one output. So if our model looks something like this


then the sequential approach is probably better.


So we access the sequential model using tf.keras.sequential. So it looks like this.


And then that initializes the sequential model. And then there are two methods for adding layers


with this model. The first of those which I'm going to show you is using the add method.


So all we do is take model add and then we add our layer here. So one of the


layers that we'll be using quite a lot is a densely connected neural network layer.


So we just write tf.keras.layers dense.


And then we're going to add in 32 units here.


And our input shape will be 10 units. So here we have 10 inputs and they are each connected to 32


units within our dense layer here. And they will use a ReLU activation function which is rectified


linear unit. Now you don't need to know all this right now but what I do want you to do is


focus on is the shape that we are producing. So if we go over to here, this is the shape of the


network that we're building. So we have 10 inputs. So that was represented by the input shape.


And then we have 32 neurons in the middle. So 32 units. If you look because it's a densely


connected neural network every single one of these is connected to the dense layer.


Every single one of these is connected to every single one. So all of our 10 inputs are connected to this unit here.


This one, this one, this one and so on. And then in our output I want to put two units.


So we'll go ahead and do that now.


So if we want to add another layer we just call add again. So we can run that. Okay sorry this is input shape.


Now if we want to add another layer all we do is model add again.


Now we only need to define the input shape in our first layer with the sequential model.


Otherwise we don't need to define it because it will assume that our input shape matches the shape of the previous layer.


Which in this case is 32. So we don't need to do anything there. So we just put two and then we add in our activation.


Now because this is the output layer and we have more than one output we will be using the softmax activation.


So in the output layer you will usually have either sigmoid or softmax. If you just have one output.


So if your output is just one value between zero and one you use sigmoid.


If you have multiple outputs so we have two units here that means our output can look something like this.


And what we will want to do is take the maximum value as the answer. So in this case it would be index 0.


So the output label would be 0 and when we're doing that we want to use the softmax activation function.


So we can execute that and then we can print out our model with the model summary.


And here we can see okay we have a sequential model and it has two layers. So this doesn't include the input layer.


So our input layer is actually 10 units and then it goes into the input layer.


This doesn't include the input layer. So our input layer is actually 10 units and then it goes into the actual model which is 32 units.


This is our hidden layer and then our output layer is two units at the end there.


And we see that there are a total of 418 parameters and all loads of trainable.


Those are the number of numbers that the model will adjust in order to learn patterns within the training data.


Okay so that is our first option for building a sequential model.


But we also have another way of building it. So rather than using the add method we can actually


define our model all within the single initialization function like this.


We just add a list and then we take what we had here.


Just the layer


and add it in. And you just add each layer as an item within the list.


And then we just execute that.


Print the model summary.


And we have the exact same model.


So that is everything on the sequential model. I hope you've enjoyed and I hope it's been useful.


Extractive Q&A With Haystack and FastAPI in Python

Okay, so we're going to go through putting together our reader model


and the fast API wrapper around all of this.


So originally I was just going to do the reader model in this video,


but I realize it's actually super easy.


So we'd almost be done already if we were just doing the reader model.


So what we're going to do is wrap everything up in the API


and then we'll add the reader model into that.


So this is where we got to before. We have this, which is our Jupyter notebook.


And this has everything.


So it has our document store, the retriever, and we're just retrieving a few things.


So we've got a few sort of contexts here.


Now, obviously, notebooks are fine for when we're putting things together


and testing things, but we need to switch over to actual


Python files. So we're going to switch across to VS code over here.


So this is our project directory on the left here.


So we have the code and the labs in here, which we've already put together.


We have our data, which is meditations.


And I also put together this requirements text file.


So in here, these are all the libraries that we need to pip install.


So we have FastAPI, Farm Haystack, which we already installed, and Uvicorn.


They're the two that I want to focus on.


So FastAPI, we're obviously using that to build our API and Uvicorn


we're going to use to spin up the server that our API will be hosted on.


So that's our requirements.txt.


I'm going to create a new folder in here.


So now I'm going to call it QA Service.


We can change these later on.


I'm not really sure what to call them all.


So we want to encode.


OK, and then in here, we'll create our first Python file.


And this is just going to be our API.


So we'll call it API.py.


And now we'll start putting together our actual API.


So let's switch across to the window.


And the first thing we need to do is import FastAPI.


So we do from FastAPI import FastAPI.


And then we want to initiate our first API.


And then we want to initialize our API here.


So just call it app equals FastAPI.


I just need to fix that there.


OK, and that initializes our API.


And then whenever we want to create a HTTP method, all we need to do is this.


So we do app.


And then in here, we write the method that we'd like to add to our API.


So the only one that I think we're going to need, at least at the moment, is a get method.


So we'll add that.


And then we also need to add the path to this method.


And in this case, we're going to be querying our Q&A model.


So we'll just create the query path.


So it's pretty good.


And then we do async.


And here we have our function.


So this is just going to be the query function.


And we want this query function to at some point accept a query.


But for now, we'll just leave it and we're going to add that in a moment.


Now, this will just return


Hello World.


So that we know that it's working.


So let's spin this up.


So I'm opening terminal in this folder at the moment.


So at the moment, we're at the high level of this.


So we'll go down a few items.


So we have, we want to go into the code directory.


And then in the code directory, it's QA service, which we made just a moment ago.


And from inside here, we initialize our Guvicorn service, which will host our API.


So for that, we just write Guvicorn.


And then we want our file name, which is API.


And then we want whatever we've called our fast API instance in our code, which is app up here.


And then we'll use the reload flag as well.


So this means that whenever we edit our code, we don't need to reinitialize the API instance in order for changes to load.


OK, so now we can see that here we have our instance.


So I'm going to copy that and open it in my browser.


So over here, enter that.


And we see this detail not found.


So that's because we just went straight to the index.


So let me just have the query endpoint and there we get Hello World.


So this is the endpoint that we're going to use for our fast API.


So we know that's working.


And now let's add in the rest of our code so that we can begin querying our QA stack.


So if we just check what we did in our Jupyter Notebook.


All we actually need to do is basically copy loads of this across.


So let me just create a new cell here.


And I'm going to take all this and we're just going to copy it across.


And honestly, this should really be all we need.


OK, so we'll copy this and take it into our code here.


So before we initialize our API, we're going to initialize the rest of our code.


So we have our document store.


We initialize that. It accesses the Aurelius index.


And then we have our retriever here.


Do we so imported them as well.


And then as well as the retriever, we also have the reader model, which is the next step.


So first, obviously, we need to import that.


And we do from Haystack reader farm import farm reader.


And then when we pull these together, we're also going to use something called a pipeline.


And to use that, we've just put from Haystack pipeline import extractive QA pipeline.


So the reason it's extractive QA is because it's question answering and we're extracting the answer from the text rather than generating it with model.


So that's why we're using the extractive QA pipeline.


Now, that should be everything that we need to import.


And then all we need to do here is we initialize our reader model.


Which is farm reader.


And then in here, we want to include our model name or path.


And that's going to be equal to deep set.


Let me do this on the middle line.


So deep set, BERT base, Cased, Squad 2.


This is a I don't know if we covered this already.


I don't think we did.


So this is a pretty standard Q&A model that I think a lot of people use.


So it's pretty safe.


I want to start with.


And then after we've initialized these three parts of our stack, we need to initialize our pipeline object.


So for that, we do pipeline.


And then we're taking our extractive QA pipeline.


And then we have our reader, which is going to be equal to our reader.


And we also need to pass the retriever, which is equal to our retriever.


So that should be everything that we need to do in order to initialize our model.


So now what we want to do is we need to query our data.


So we do pipeline.run.


And then in here, I'm just going to put like a random query.


So what was the one we used before?


What did your grandfather teach you?


OK.


And just pass that as the query.


And then this will return.


This is going to return a dictionary.


So then we can just return it straight away.


Now, let's see if our API is still running.