Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Oxygen in a package #115

Closed
JanisErdmanis opened this issue Jun 10, 2023 · 5 comments
Closed

Using Oxygen in a package #115

JanisErdmanis opened this issue Jun 10, 2023 · 5 comments

Comments

@JanisErdmanis
Copy link
Contributor

I love the idea of writing HTTP request handlers and simultaneously registering them to a router. I would also love to try to use Swagger to document my service currently written in bare HTTP.jl, as can be seen here. However, I have stumbled upon an issue where my handlers are not registered when it's imported from the module.

For example, let's consider a module:

module Example

using Oxygen
using HTTP

get("/") do request::HTTP.Request
    return "hello world!"
end

serve() = Oxygen.serve(port=8081)

end

Using this module as import Example; Example.serve() would serve a blank page at /.

Interestingly, I found that if I import the module as a file include("src/Example.jl"); Example.serve(), the handler works as expected. Also, I found that moving the handler to the __init__ fixes the issue; however, that is not a viable solution as that would either make __init__ function unbearably long or would make code less local in comparison with direct usage of HTTP.

@ndortega
Copy link
Member

ndortega commented Jun 10, 2023

Hi @JanisErdmanis,
No worries, that's actually intended behavior.

In Julia, importing a module does not run all the code defined inside it at runtime. Instead, it only loads the module into memory and makes its exported functions and variables available for use in the current scope.

If you want to run all the code defined inside a module, you can use the include() function. This function reads and evaluates the contents of a file as if they were entered directly into the REPL or script.

You can read more about this here:
https://docs.julialang.org/en/v1/manual/code-loading/

So when integrating Oxygen into a custom module, you'll need to wrap the code that registers routes with some function. You can use the __init__ function to have it called automatically or create your own function that would need to get called manually. Alternatively, you can load your custom module with include() like you're already doing here: https://github.com/PeaceFounder/PeaceFounder.jl/blob/master/src/PeaceFounder.jl#L24

This line is what executes all the code in your module and registers your routes automatically. If you take a similar approach with oxygen, it should work as expected.


Below is a brief example of how you could get this to work in your package. Break out the api operations into its own module and then include it, which will register all your routes.

image

@JanisErdmanis
Copy link
Contributor Author

This still does not work as I do load PeaceFounder as a package. However, I just found that I can use @eval at the __init__ block to include my service module:

module Example

function __init__()
    fname = (@__DIR__) * "/Service.jl"
    @eval include($fname)
end

serve() = Service.serve()

end 

and that works as expected. Could there be any downsides to using this approach?

Also, thanks for pointing out to code-loading section. That has made me confused lately.

@ndortega
Copy link
Member

ndortega commented Jun 11, 2023

Honestly, I like that solution you came up with. It's short, clear, and is scalable ( it should automatically execute any other code referenced from that module ).

The only downside I'd be wary of is any weird precompilation issues from using eval in your package. Granted, this should be ok since you're only evaluating your own code that's tracked in git. This usually becomes an issue when people try to evaluate stuff that can change during run-time.

Here's the specific use case and discussion I found:
https://discourse.julialang.org/t/precompilation-init-and-eval/70188

On a side note, the following package isn't using this new approach we're talking about, but it could still be helpful to see a package that uses Oxygen called RemoteHPC by Louis Ponet: https://github.com/louisponet/RemoteHPC.jl/blob/master/src/api.jl#L38

@JanisErdmanis
Copy link
Contributor Author

JanisErdmanis commented Jun 12, 2023

I am honestly hesitant to use @eval + include approach I proposed earlier. The reason is that execution at the runtime is nonessential when compared with using HTTP directly. In my opinion, there is a missing Julia feature but I am not sure what it is.

Another approach I am considering is to include the Oxygen as a local module:

module OxygenExample

include(Base.find_package("Oxygen"))
using .Oxygen

using HTTP

get("/") do request::HTTP.Request
    return "hello world!"
end

serve() = Oxygen.serve(port=8081)

end 

which works fine, as expected. The only downside I see is that I need to manually add Oxygen's dependencies for that to work, but I expect that could be automated within a function.

@ndortega
Copy link
Member

That's a pretty wild approach. I had no idea we could add packages as local modules in Julia!

Although, I would caution you against going that route unless you absolutely had to. If I ever have to add a new dependency and publish it - it could break your package and require you to republish your app with the new dependency. I personally wouldn't want my package to be coupled so tightly to another package.

Granted, you could get around this by explicitly adding strict version rules in the compatibility section of your Project.toml file.
This would prevent any potential breaking changes from getting introduced at the cost of your users not having access to the latest updates from Oxygen (until you manually update your compatibility section).

But at the end of the day, It's your package and you should choose whatever approach best meets your requirements. I'm just thankful that you see Oxygen as a viable alternative to HTTP.jl and am excited to see what you can do with it! Let me know if you have any other questions or feature requests. Package authors are the power users of our little community and I want to make sure to support them in any way that I can.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants