-
-
Notifications
You must be signed in to change notification settings - Fork 662
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
(asys) API #8832
base: development
Are you sure you want to change the base?
(asys) API #8832
Conversation
As a general rule, nothing in std outside the
By transitive property, it also applies to:
|
#8817 has this TODO:
How does that affect this PR? |
How is importing
That's a TODO for the eval-specifics only. The public API remains the same. |
I don't know, I figured that imports were there for a reason. If they are unneeded we can just remove them.
Ok, but what does "shared code" refer to then? |
Note that |
Yes, it's my bad. They are not unneeded, they are generally there for
"shared code" is e.g. in
My bad, the fallback is |
I'm trying to use
As a consequence, I don't think This also makes it questionable if |
var cb:Callback<NoData> = (err) -> trace("error!", err); | ||
``` | ||
**/ | ||
@:from public static inline function fromErrorOnly(f:(error:Error) -> Void):Callback<NoData> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this and fromErrorResult
should not exist. It's a source for NPEs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you give an example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure
var cb:Callback<String> = function(error:Error) { // It's clear `null` is not expected here
switch(error.type) {...} // null pointer error
}
successOp(cb);
var cb:Callback<String> = function(_, result:String) { // It's clear `null` is not expected here
trace(result.length); // null pointer error
}
failOp(cb);
And it defeats null safety feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the first would compile, since fromErrorOnly
only produces a Callback<NoData>
.
I would argue the second is a user error. Subverting null safety sounds more serious though…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the first would compile, since fromErrorOnly only produces a Callback.
Data type does not matter in that example. It's still NPE even with Callback<NoData>
. Maybe implementation should be changed to if(err != null) f(err)
.
I would argue the second is a user error.
That's a user error which could never happen if Null<>
is respected by the API ;)
asys.io.FileReadStream.FileReadStreamOptions; | ||
|
||
/** | ||
This class provides methods for synchronous operations on files and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit weird to have a completely synchronous API in asys
package
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, we're not going to have multiple packages for this, and we cannot merge it with sys
easily (more so due to asys.io.File
incompatibilities with sys.io.File
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But
The types in asys will only be available on libuv-backed system targets
while asys.FileSystem
and such are perfectly doable without libuv.
we're not going to have multiple packages for this
I think we should split it. Maybe io.sync
and io.async
or whatever better names?
|
||
/** | ||
Tests specific user permissions for the file specified by `path`. If the | ||
check fails, throws an exception. `mode` is one or more `FileAccessMode` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to specify the type of exceptions.
The result of this call should not be used in a condition before a call to | ||
e.g. `open`, because this would introduce a race condition (the file could | ||
be deleted after the `access` call, but before the `open` call). Instead, | ||
the latter function should be called immediately and errors should be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this really guarantee avoidance of race conditions?
And in case one just needs to check access without immediately opening a file it forces a try..catch
instead of a simple check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And actually one can just handle exceptions on open
to find out if required access is not permitted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related comment: #8832 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's what the comment is trying to say, maybe I worded it badly. Basically, if you need a file, you should always try { var file = open(...); /* use file */ } catch ...
rather than access
, then open
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But what's the purpose of such access()
then?
`access` with the `mode`: | ||
|
||
```haxe | ||
FileAccessMode.Execute | FileAccessMode.Read |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to get which flag was rejected in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, uv basically wraps access(2)
, which will only error out with EACCES
. The only way would be to check the flags individually.
the latter function should be called immediately and errors should be | ||
handled with a `try ... catch` block. | ||
**/ | ||
static function access(path:FilePath, ?mode:FileAccessMode = FileAccessMode.Ok):Void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think mode
should be mandatory (preferably) or at least FileAccessMode.Read
as it's usually useless to know the file is there and to not know if any action on it could be performed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this function supposed to be used? It returns Void
, so I'm assuming it throws in case something is wrong. But then the documentation section about using the "result of this call" makes no sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is a throwing function. I guess I thought of "result of this call" not as strictly the return value.
|
||
TODO: `followSymLinks == false` is not implemented and will throw. | ||
**/ | ||
static function chmod(path:FilePath, mode:FilePermissions, ?followSymLinks:Bool = true):Void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this supposed to be recursive?
|
||
TODO: `followSymLinks == false` is not implemented and will throw. | ||
**/ | ||
static function chown(path:FilePath, uid:Int, gid:Int, ?followSymLinks:Bool = true):Void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it recursive?
static function chown(path:FilePath, uid:Int, gid:Int, ?followSymLinks:Bool = true):Void; | ||
|
||
/** | ||
Copies the file at `src` to `dest`. If `dest` exists, it is overwritten. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we change this to be explicit about rewriting an existing file? Maybe as an additional argument rewrite:Bool = false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The libuv call itself has no such argument, which means we would have to do something like:
if (rewrite || !FileSystem.exists(target))
copyFile(...);
else
throw "already exists";
Which introduces a race condition.
Passing `null` as a path to any of the functions in this class will result | ||
in unspecified behaviour. | ||
**/ | ||
extern class FileSystem { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's have a method for recursive dir copy in std already, please? )
|
||
The result of this call should not be used in a condition before a call to | ||
e.g. `open`, because this would introduce a race condition (the file could | ||
be deleted after the `exists` call, but before the `open` call). Instead, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't need to check file existance if I need to open it and handle exceptions anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, yes, that's what the comment says. Do you understand it differently?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I read it (and in access
doc too) as "instead of a check after exists
, call open
immediately".
**/ | ||
static function exists(path:FilePath):Bool; | ||
|
||
static function link(existingPath:FilePath, newPath:FilePath):Void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a symlink or hardlink?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it throw if newPath
already exists?
|
||
/** | ||
Creates a unique temporary directory. `prefix` should be a path template | ||
ending in six `X` characters, which will be replaced with random characters. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the behavior of prefix
is not ending with XXXXXX
?
Isn't it more convenient for a user to just provide whatever prefix
which just gets appended with a random characters?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I find APIs with argument restrictions like that really stupid... I guess the C API is like that because you don't know string lengths in C, so the native function doesn't know how much to allocate for the applied template. We don't have to carry such awkwardness over into our API though.
|
||
The generated directory needs to be manually deleted by the process. | ||
**/ | ||
static function mkdtemp(prefix:FilePath):FilePath; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add ?mode:FilePermissions
|
||
/** | ||
Renames the file or directory located at `oldPath` to `newPath`. If a file | ||
already exists at `newPath`, it is overwritten. If a directory already |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer overwrite:Bool = false
instead of auto-overwriting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meh, that would be impractical I think. We could add the flag but it should default to true
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with defult true
, but the flag should be implemented.
Afaik filesystem paths are not lockable in general, so without such a flag we have no way to guarantee that some sensible data won't be overwritten on rename.
Pipes created between the current (host) process and `this` (spawned) | ||
process. The order corresponds to the `ProcessIO` specifiers in | ||
`options.stdio` in `spawn`. This array can be used to access non-standard | ||
pipes, i.e. file descriptors 3 and higher, as well as file descriptors 0-2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add public var pipes(get,never):Array<Pipe>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the difference? That is basically what stdio
is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The difference is stdio
also contains stderr, stdin, stdout. We have separate fields for those, but not for pipes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stdio
contains all the descriptors (This array can be used to access non-standard pipes, i.e. file descriptors 3 and higher, as well as file descriptors 0-2
). It is indexed by fd, so it will naturally contain stderr, stdin, and stdout as 0, 1, and 2 (assuming they were created during spawn).
@@ -0,0 +1,34 @@ | |||
package asys; | |||
|
|||
class Timer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add static function interval(f:()->Void, timeMs:Int):Timer
which repeatedly executes f
each timeMs
milliseconds.
`callback:Callback<T>` argument, where `T` is the return type of the | ||
synchronous method. | ||
|
||
Errors are communicated through the callbacks or in some cases thrown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to redirect all errors to callbacks so that one doesn't need to have two error-handling mechanisms for a single function call?
Great work! |
uni-directional or bi-directional, depending on how it is created. Pipes can | ||
be automatically created for spawned subprocesses with `Process.spawn`. | ||
**/ | ||
class Socket extends Duplex { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at a variety of "TCP only" and "IPC only" methods on options I think it would be better to have separate types like TcpSocket
and IpcSoket
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe with a common ancestor.
/** | ||
Error type, usable for discerning error types with `switch` statements. | ||
**/ | ||
public final type:ErrorType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like enum-based errors in Haxe. Any additional error type is a breaking change...
std/haxe/async/Defer.hx
Outdated
Convenience shortcut for `Timer.delay(f, 0)`. | ||
**/ | ||
public static inline function nextTick(f:() -> Void):asys.Timer { | ||
return asys.Timer.delay(f, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this PR is about API, not implementations. But just be aware that haxe.Timer
on java throws runtime errors if delay is 0ms. And small delays like 1ms don't guarantee the function won't be executed on the same tick.
See travis builds for the latest two commits here for example: https://github.com/haxe-utest/utest/commits/master?after=61cfb1d9997eaf18ebcd6c9f4a417a832e875116+0
`intermediate` is `null`, it is treated as an empty array and `input` is | ||
connected directly to `output`. | ||
**/ | ||
public static function pipeline(input:IReadable, ?intermediate:Array<IDuplex>, output:IWritable):Void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, avoid optional args followed by mandatory args.
Discussion about removing skippable args feature keep coming. So let's not have an API which relies on it.
This PR mentions "abstract base class which should not be instantiated" multiple times. |
/** | ||
Path to the working directory. Defaults to the current working directory if | ||
not given. | ||
**/ | ||
?cwd:FilePath, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "short notation" of structs doesn't support doc comments.
Passing `null` as a path to any of the functions in this class will result | ||
in unspecified behaviour. | ||
**/ | ||
extern class FileSystem { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing absolutePath
method. E.g.: https://api.haxe.org/sys/FileSystem.html#absolutePath
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I forgot about it. Such a method should exist on the haxe.io.FilePath
type, because it does not involve actually interacting with the OS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is no way to get the absolutePath without asking the OS where you are right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is also true. There should still be a method that normalises a filepath, even without sys access – so it can resolve e.g. x/../y/../z/a/b/..///..
to z
. If the path happens to be absolute to begin with, it should remain absolute.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about: https://api.haxe.org/haxe/io/Path.html#normalize
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lublak Indeed, that is exactly what I meant ^^ I forgot it already exists in the APIs.
This PR is just the Haxe part of the new asys APIs. This includes:
asys
- many functions were stubbed out an markedextern
since the actual implementations will be added with target-specific asys PRshaxe
:Error
,ErrorType
haxe.io
:Duplex
,FilePath
,IDuplex
,IReadable
,IWritable
,Readable
,StreamTools
,Transform
,Writable
The types in
asys
will only be available on libuv-backed system targets (new defineasys
,pf_asys
in OCaml).